3

I am trying to switch normal program flow while an interrupt returns:

START
    SEI
    LDX #<IRQ
    LDY #>IRQ
    STX $FFFE
    STY $FFFF
    CLI

LOOP1
    INC $D020
    JMP LOOP1

LOOP2
    INC $D021
    JMP LOOP2

IRQ
    STA SAVEA+1
    STX SAVEX+1
    STY SAVEY+1

    // Some Routines

    LDA #$00
    PHA
    LDA #<LOOP2
    PHA
    LDA #>LOOP2
    PHA

SAVEA   
    LDA #$00
SAVEX   
    LDX #$00
SAVEY   
    LDY #$00
    RTI

I wrote this code accourding to that source: http://6502.org/tutorials/interrupts.html#1.3

enter image description here

But PHA's cause crash, how to switch normal flow LOOP1 to LOOP2 in an interrupt?

Digerkam
  • 1,826
  • 4
  • 24
  • 39
  • 2
    @DavidHoelzer you misunderstood the code, that part is just setting up the interrupts, and the two loops are the two processes he wants to schedule. That said, disabling further interrupts in the handler sounds like a good idea. Also, the saved stuff already on the stack should be removed, otherwise you will get a stack overflow if you just keep pushing things. The order seems wrong too, you need to use the same as on the diagram. – Jester Feb 02 '16 at 23:13
  • Oh yes, you're right. I overlooked the < and > for the IRQ code. It has been quite a while. :) – David Hoelzer Feb 02 '16 at 23:14
  • 3
    It is unclear whether you are trying to manipulate the stack content or write self-modifying code. You have pushed a new return address, but the previous is still there. You will rapidly run out of stack. I suggest `PHA; TXA; PHA; TSX` and then modifying the stacked return address by indexing `X`, before restoring the registers and doing a `RTI`. – Weather Vane Feb 03 '16 at 00:15
  • You do realize that doing this will most likely lead to very unintended results for anything but the simplest of routines like your loops, without use of registers, stack or status flags other than what can be overwritten by the other routine at any moment? You would be better off setting some flag in memory inside your interrupt handler and performing controlled checks of this value inside your loops and branching accordingly. – Lars Haugseth Apr 26 '16 at 23:33

3 Answers3

4

The simplest thing is probably to have two stack areas -- one for every task. $100-$17f and $180-$1ff, for example. Then, you would have your interrupt task switching code like this:

  pha
  txa
  pha
  tya
  pha ;saving task's registers on its stack,
      ;where flags and PC are already saved
      ;by entering the interrupt

  tsx
  stx ... ;save task's stack position

  ... ;select new task to run/etc.

  ldx ...
  txs ;load other task's stack position

  pla
  tay
  pla
  tax
  pla ;restore other task's registers

  rti ;and finally continue other task 
lvd
  • 793
  • 3
  • 12
  • 1
    That's a neat approach. Maybe it is worth pointing out hardware isn't enforcing any stack boundaries. So if B ends up in A stack space, A can start doing very odd things. The task switcher can do some range integrity checks. But some wraparound mistakes are still undetectable. – Core Feb 03 '16 at 23:31
  • 1
    @lvd, TSX is the key for the solution I wrote below, I tested and it works! Thank you! – Digerkam Feb 04 '16 at 00:39
  • 1
    The hardware isn't enforcing stack boundaries generally; the only difference is that the limit is 128 bytes rather than 256 and you'll stomp the top of somebody else's stack instead of your own if you silently overrun. – Tommy Feb 04 '16 at 15:23
2

Simple way is:

TSX
LDA #$00
STA $0101,X   // Processor Status
LDA #<LOOP2
STA $0102,X   // Task Low Address
LDA #>LOOP2
STA $0103,X   // Task High Address

But for more complex task management, we have to save A,X,Y registers for each task:

START
    SEI
    LDX #<IRQ
    LDY #>IRQ
    STX $FFFE
    STY $FFFF
    CLI

LOOP1
    INC $D020
    JMP LOOP1

LOOP2
    INC $D021
    JMP LOOP2

IRQ
    STA $FF
    STX $FE
    STY $FD
    LDX TASK+1
    CPX TASK
    BEQ CONT
    LDY TASKI,X
    TSX
    LDA $0101,X
    STA TASKS+0,Y
    LDA $0102,X
    STA TASKS+1,Y
    LDA $0103,X
    STA TASKS+2,Y
    LDA $FF
    STA TASKS+3,Y
    LDA $FE
    STA TASKS+4,Y
    LDA $FD
    STA TASKS+5,Y
    LDA TASK
    STA TASK+1
CONT

    // Change Task
    LDA TASK
    CLC
    ADC #$01
    AND #$01
    STA TASK


    LDX TASK
    CPX TASK+1
    BEQ CONT2
    STX TASK+1
    LDY TASKI,X
    TSX
    LDA TASKS+0,Y
    STA $0101,X
    LDA TASKS+1,Y
    STA $0102,X
    LDA TASKS+2,Y
    STA $0103,X
    LDA TASKS+3,Y
    STA $FF
    LDA TASKS+4,Y
    STA $FE
    LDA TASKS+5,Y
    STA $FD
CONT2
    LDA $FF
    LDX $FE
    LDY $FD
    RTI

TASK
    .BYTE 0,0
TASKI
    .BYTE 0,6,12,18,24,30,36
TASKS
    .BYTE 0,<LOOP1,>LOOP1,0,0,0
    .BYTE 0,<LOOP2,>LOOP2,0,0,0
Digerkam
  • 1,826
  • 4
  • 24
  • 39
  • 1
    Won't there be a problem with `tsx:lda $101,x:etc.` code when the stack has just happened to wrap around from $100 to $1ff? Probably not for your case, where you thoughtfully divide stack space, but in general, if the stack is used only for interrupt stacking, procedures calling and eventual PHA/PLA and there is no evident need to worry about exact stack position at all. – lvd Feb 04 '16 at 10:09
  • @lvd Yes, it would be a problem if you underflowed the stack. But 256 bytes of stack space on a 6502 are more than enough. I've rarely ever had the stack pointer drop below `$C0` – puppydrum64 Nov 23 '22 at 11:47
1

I'm not sure exactly what you're trying to do, but it looks to me as though you want to change the background colors on a Commodore 64 in rapid succession. If you want to change what you're doing, there's actually a much easier way:

START
    SEI
    LDX #<IRQ
    LDY #>IRQ
    STX $FFFE
    STY $FFFF
    CLI

LOOP1
    INC $D020   ;the interrupt will switch this to $D021 and back every time it happens.
    JMP LOOP1

IRQ
    PHA
       LDA #$01
       EOR LOOP1+1  ;the value at this address is the "20" in "INC $D020"
       STA LOOP1+1  ;toggle between "INC $D020" and "INC $D021" each IRQ
    PLA
    RTI

This has a lot less overhead than setting a flag and branching based on that flag. Presumably your goal is to update the border color/background color as quickly as possible and this greatly reduces the amount of time spent checking conditions.

puppydrum64
  • 1,598
  • 2
  • 15