6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Nov 10, 2024 5:21 pm

All times are UTC




Post new topic Reply to topic  [ 14 posts ] 
Author Message
 Post subject: Interrupt Handler
PostPosted: Sun Feb 24, 2008 11:16 pm 
Offline

Joined: Thu Jul 05, 2007 4:11 pm
Posts: 33
Hi,

I have my interrupt handler working and enter it using a BRK instruction, but when I exit the handler, it just returns right back to the handler again, altough I have made certain that the interrupt flag is being cleared with CLI. I clear it at the beggining of my handler.

Any suggestions?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Feb 25, 2008 12:10 am 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
It would be helpful if you'd post (or provide a link to) the code containing the BRK and the BRK/IRQ handler you're using. Even if we can't see the issue just by looking at it, we could at least try the code out (assuming it's relatively short).


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Feb 25, 2008 1:37 am 
Offline

Joined: Tue Dec 30, 2003 10:35 am
Posts: 42
You are aware that BRK skips the byte after the BRK opcode, right? So this prints 1, not 2 as you might expect:

LDX #0
BRK
INX ; This is skipped
INX
JSR PRINT_X ; Prints 1
...

IRQ:
RTI


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Feb 25, 2008 2:30 am 
Offline

Joined: Thu Jul 05, 2007 4:11 pm
Posts: 33
Quote:
You are aware that BRK skips the byte after the BRK opcode, right? So this prints 1, not 2 as you might expect:

No, I wasn't aware of that. That is most likely my problem then. Would is the standard practive... to put a NOP there then?

Nope... that wasn't it....


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Feb 25, 2008 2:39 am 
Offline

Joined: Thu Jul 05, 2007 4:11 pm
Posts: 33
Here is the section that initiates the BRK

Code:
monitor   BRK          ;Enter ML Monitor   
          JSR cls      ;clears screen
          JSR menu     ;displays menu
          JMP mainlp   ;jumps to main program loop


JSR cls
JSR menu
and JMP mainlp never gets executed... because it retruns me to BRK again.


Here is my IRQ handler
Code:
; BRK Handler----------------------------------------------------------------------------------
break   CLI                     ; clear interrupt
        STA   acc               ; save A   
        STX   xreg              ; save X
        STY   yreg              ; save Y
        PLA                     ;
        STA   status            ; save P
        PLA                     ; pull PCL off stack
        PLX                     ; pull PCH off stack
        SEC                     ;
        SBC   #$02              ;
        STA   pclo              ; backup to BRK cmd
        BCS   brk2              ;
        DEX                     ;
brk2    STX   pchi              ; save PC
        TSX                     ; get stack pointer
        STX   stack             ; save stack pointer
        ;jsr   Bell             ; Beep speaker
        LDX   #$FF              ;
        TXS                     ; clear stack
        JSR   mlmon             ; starts ML Monitor
        RTI                     ; exit break handler


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Feb 25, 2008 3:04 am 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
DonnaD wrote:
Would is the standard practive... to put a NOP there then?


Some BRK handlers look at the byte after BRK (by getting PC from the stack), but BRK and RTI ignore it, so it doesn't matter what its value is. NOP works as well as anything else.

RTI pulls P and PC (lo byte, then hi byte) from the stack, so when you pull them off with your BRK handler, you've got to put them back on before the RTI. Something like:

Code:
     LDX pchi
     LDA pclo
     CLC
     ADC #2
     BCC skip
     INX
skip PHX
     PHA
     LDA status
     PHA
     RTI


In fact, in the above, you could replace:

Code:
     CLC
     ADC #2
     BCC skip


with:

Code:
     INC
     BNE skip


and then there wouldn't need to be a dummy byte after the BRK.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Feb 25, 2008 4:00 am 
Offline

Joined: Thu Jul 05, 2007 4:11 pm
Posts: 33
That first code works... but if I subtract one instead of two from the PC to avoid the ignored byte, it doesn't work.

Also, is this a bug? and if so is it in all 6502's... I have a W65C02S


Last edited by DonnaD on Mon Feb 25, 2008 4:04 am, edited 2 times in total.

Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Feb 25, 2008 4:01 am 
Offline

Joined: Tue Dec 30, 2003 10:35 am
Posts: 42
DonnaD, your fix would back the PC back up to point to the BRK again, since it subtracts 2 from it. BRK only skips one extra byte, so if you wanted to continue execution just after the BRK opcode, you'd only subtract 1. Even if it were properly subtracting 1, it's still changing the stack pointer and not putting the return address back. RTI pops a byte, puts that into the status flags, then pops two more and jumps to the address formed by them.

Any reason you can't just put a NOP after your BRK? That's a much simpler solution. Also, the best way to get this kind of thing working is to start with something known to work, then incrementally add things until it breaks. So in this case, you'd start with a minimal BRK handler of just RTI. Once you understand the behavior of it, then add more to the handler. Also, when testing things where instructions seem to be skipped, put a bunch of one-byte instructions in the area you think skipping is occurring, so you ensure that if it is skipping, it's not falling in the middle of an instruction. In the example I used INX, since it also tells us how much is being skipped.

The following (verified to work) BRK handler avoids skipping the byte after BRK:
Code:
irq:
    ; Save A and P
    sta saved_a
    pla
    sta saved_p
   
    ; Decrement return address
    pla
    bne :+
    sta pclo
    pla
    dea
    pha
    lda pclo
:   dea
    pha
   
    ; Put P back on stack
    lda saved_p
    pha
   
    ; Do whatever, but don't change stack pointer
    ; ...
   
    ; Restore A and P, then return to caller
    lda saved_a
    rti


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Feb 25, 2008 4:38 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 9:02 pm
Posts: 1746
Location: Sacramento, CA
DonnaD wrote:
Here is my IRQ handler
Quote:
; BRK Handler----------------------------------------------------------------------------------
break CLI ;clear interrupt
STA acc ; save A
STX xreg ; save X
STY yreg ; save Y
PLA ;
STA status ; save P
PLA ; pull PCL off stack
PLX ; pull PCH off stack
SEC ;
SBC #$02 ;
STA pclo ; backup to BRK cmd
BCS brk2 ;
DEX ;
brk2 STX pchi ; save PC
TSX ; get stack pointer
STX stack ; save stack pointer
;jsr Bell ; Beep speaker
LDX #$FF ;
TXS ; clear stack
JSR mlmon ;starts ML Monitor
RTI ;exit break handler


DonnaD,

My Break routine was not designed to return you to the location following the break. It was meant to be used in troubleshooting to halt a program and save the register contents for examination by the System Monitor.

The LDX #$FF and TXS at the end essentially wipe out the Stack and reset it before entry into the Monitor.

There are ways to get the return address from the stack without the PLA and PHA.

Code:
...
BRK:
     STA  Save_Acc
     STX  Save_X
     STY  Save_Y
     TSX
     LDA  $101, x
     STA  Save_P
     LDA  $102, x
     STA  Save_PCL
     LDA  $103, x
     STA  Save_PCH
     LDX  Save_X
     LDA  Save_A
     RTI


This will retrieve the data without changing the stack pointer and return you to the 2nd byte after the BRK instruction. You can build on this to meet your needs.

Hope that clears up why my code wasn't working for you.

Daryl


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Feb 25, 2008 5:31 am 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
DonnaD wrote:
That first code works... but if I subtract one instead of two from the PC to avoid the ignored byte, it doesn't work.


Code:
monitor BRK


pushes the high byte of monitor+2, then pushes the low byte of monitor+2, then pushes P, then jumps to the BRK handler. Using your original BRK handler, you pull P and the address from the stack, subtract 2 from that address and store the result in pclo and pchi. So pclo contains the low byte of monitor, and pchi contains the high byte of monitor.

RTI pulls P from the stack, the pulls the low byte of the return address, then pulls the high byte of the return address, then jumps to the return address.

So using my first routine, 2 is added 2 the 16-bit value whose low byte is in pclo and whose hi byte is in pchi, then the high byte of the result is pushed onto the stack, then the low byte of the result is pushed onto the stack, then P is pushed onto the stack, then comes RTI. Since pclo and pchi contained monitor, the return address pushed onto the stack is monitor+2, so RTI skips the byte after the BRK. My proposed change adds 1 instead of 2, so the return address would be monitor+1 instead of monitor+2

If the mlmon routine doesn't care about the value of pclo and pchi, then you can subtract 1 (instead of 2) from the address you pull from the stack and store that in pclo and pchi. Then push pchi and pclo onto the stack (without adding 1 or 2) before the RTI, since pclo and pchi contain the low and high bytes of monitor+2-1 = monitor+1 which is the address you want to return to.

Also, make sure that you're subtracting 1 correctly. Remember that:

Code:
     DEC lo_byte
     BNE skip
     DEC hi_byte
skip


doesn't decrement properly. Instead, you need something like:

Code:
     LDA lo_byte
     BNE skip
     DEC hi_byte
skip DEC lo_byte


DonnaD wrote:
Also, is this a bug? and if so is it in all 6502's... I have a W65C02S


No, it's not a bug. BRK was intentionally designed to skip the byte after the BRK on all members of the 6502 family, from the original NMOS 6502 to the 65C816.

blargg wrote:
Any reason you can't just put a NOP after your BRK? That's a much simpler solution.


It makes the BRK handler much simpler. However, on rare occasions it's more convenient for BRK to act like a one byte instruction than a two byte instruction, even though it's slower and it makes the BRK handler less simple. Even so, I'd also recommend using BRK and a dummy byte as a starting point, and modifying the BRK handler not to skip the byte after BRK only when necessary.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Feb 25, 2008 5:49 am 
Online
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8538
Location: Southern California
DonnaD, it is not clear from the little you wrote if you are really understanding the I flag and the CLI instruction. CLI does not clear an interrupt condition. Non-BRK interrupts are generated by hardware, and the interrupt condition needs to be cleared by the interrupt-service routine doing whatever the particular hardware requires in order to clear the condition. CLI clears the interrupt-disable flag, making the processor ready to accept hardware interrupts again through its IRQ\ input. This is addressed in my interrupts article at http://www.6502.org/tutorials/interrupts.html . At least enjoy my cartoons if nothing else. ;)

In your code
Code:
monitor   BRK          ;Enter ML Monitor   
          JSR cls      ;clears screen
          JSR menu     ;displays menu
          JMP mainlp   ;jumps to main program loop

the first JSR will get skipped; so if the low byte of the cls address is 00, the processor would see that as another BRK instruction.

The second byte after the BRK is intended to be a signature byte, so the BRK service routine can optionally read it to help decide what it's supposed to do with this particular BRK. It is not a bug. The 65c02 has all the bugs fixed, unlike the NMOS 6502. The only time I've used BRK was when we used the AIM-65 computers in school 25 years ago, and then we always put two in a row.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Wed Feb 27, 2008 7:58 pm 
Offline

Joined: Thu Jul 05, 2007 4:11 pm
Posts: 33
Thanks Garth,

I think I am understanding the I flag properly. Do I not need to use it with BRK, even though I am not using hardware interrrupts?

I would understand that when my software issues a BRK it will set the I flag and enter the BRK handler. Once in the handler, if I don't reset the I flag, it will immediatly execute BRK again since it things it was issued again since I never cleared the I flag.

If I use hardware interrupts, I would have to clear the I flag, as well as the flag on the external hardware... i.e. the 6522 or whatever.

Tell me if I'm right!


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Wed Feb 27, 2008 8:40 pm 
Online
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8538
Location: Southern California
Remember-- the I flag is the interrupt-disable flag, so clearing it enables interrupts. BRK, NMI, and IRQ set I. BRK only occurs with the BRK instruction, so regardless of the flag's status, your BRK routine will not be re-entered until the next BRK instruction, even if you leave I alone.

Interrupts set I, and RTI normally clears it (although you can change that by changing the status byte on the stack that RTI pulls off and puts back in P). I've had up to about 100,000 interrupts per second (on purpose-- not from a bug) while not using SEI or CLI for several minutes at a time.

Most interrupt-service routines should not have CLI or SEI in them. The place to use CLI is after you set up your interrupt sources and you're ready to start servicing them. SEI is mainly used when you're entering a piece of code that must not be interrupted, whether for timing reasons or whatever.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Feb 28, 2008 4:18 am 
Offline

Joined: Thu Jul 05, 2007 4:11 pm
Posts: 33
OK.. I get it now... I almost had it but not quite.... that makes sense now.

thanks


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 14 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: