6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Sep 22, 2024 4:28 pm

All times are UTC




Post new topic Reply to topic  [ 81 posts ]  Go to page 1, 2, 3, 4, 5, 6  Next
Author Message
 Post subject: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 4:12 pm 
Offline
User avatar

Joined: Sat Aug 22, 2015 8:54 pm
Posts: 31
After reading the topic dipping a toe into interrupts, I wondered if it is possible to have multiple interrupt vectors like in x86. Obviously there will be quite some complexity to it, but will it be too complex? Is it better to use JSR/RTS?

_________________
It may look hard, but it won't take long if you take it one byte at a time.


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 4:27 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10938
Location: England
Without adding hardware, the usual approach is to allow multiple sources of IRQ and for the interrupt handler to check the possible sources in priority order. Once the source is identified it might be possible to clear the interrupt mask, even while still within the interrupt service routine, to allow for another interrupt to be taken during the processing of this one. Not sure if that is widely done - I suspect it's generally avoided.

To vector interrupts, I think you'd use the VPB pin and some logic to tweak (or generate) the interrupt vector bytes as they are read. Then you can have a unique service routine for each source.


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 4:34 pm 
Offline
User avatar

Joined: Sat Aug 22, 2015 8:54 pm
Posts: 31
Just had another thought - what if instead I had a handler at say $200 - and had the A register set to a specific vector, #$00 - #$FF. Then I could simply compare the A register (after a BRK is executed) to certain values and have a vector table that shows addresses to jump to from there. The qustion is, is this at all effecient?

_________________
It may look hard, but it won't take long if you take it one byte at a time.


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 4:57 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10938
Location: England
Not quite sure what you mean - when do you want to set the A reg? Ah, maybe you are intending to do something with software interrupts using BRK? In that case, the 6502 puts you into the IRQ handler, and you have to check the B bit in the saved status register. If it was a BRK, you can retrieve the saved PC and then read off the operand byte, but that's quite fiddly. As you say, you could adopt a convention that you'll load A with a byte to distinguish your service calls - that's a reasonable plan. You could indeed then have a table of handlers to jump to, or a series of branches, or whatever works for you.

The OS in Acorn's Beeb has a number of complex OS calls which are distinguished by the content of A, but they do that with a JSR/RTS rather than a BRK. There's a one-byte gain in using BRK, but it costs a cycle in taking the interrupt and more cycles in the IRQ/BRK handler. So BRK is not widely used as a SWI.


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 5:13 pm 
Offline
User avatar

Joined: Sat Aug 22, 2015 8:54 pm
Posts: 31
What I mean is that I want to set the A register and then call BRK which puts the processor in the IRQ handler like you said. It does seem quite a bit more efficient than JSR/RTS (I am writing a BIOS and want to have interrupt vectors instead of having addresses to memorize and JSR to). So for the equivalent of the following x86 code:
Code:
mov ah, 03h
int 10h
; Code continues....

I could have:
Code:
lda #$10
ldy #$03
brk
; Code continues....


Or something like such. This way I could pass parameters, data, etc. via the registers and/or the stack or even a RAM location. Then I would need a handler for IRQ/BRK in the BIOS, and to call a function the user would simply load A, X, Y, etc. and then BRK.

Another thing: if I used the above method, would it be worth it or would it still be more efficient in terms of clock cycles (running 20MHz system clock) to JSR/RTS or even leave it up to the user to write system calls?

_________________
It may look hard, but it won't take long if you take it one byte at a time.


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 5:23 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10938
Location: England
OK, you can certainly do that. The Beeb's OS often uses X and Y to hold a pointer to a control block in memory, so you don't have to have a fixed location to pass data.

One thing to bear in mind is that BRK takes a one byte operand. If your assembler doesn't know that, you'll need to follow your BRK with something like a NOP. As I mentioned before, getting hold of that operand byte is a bit fiddly so mostly it's ignored.

If you check the Beeb's API at
http://www.atomclub.nl/sites/Fpga/www.h ... BBC_09.htm
or perhaps better at
http://www.sprow.co.uk/bbc/reference.htm#OSWords
you'll notice that hundreds of OS calls are behind just a handful of addresses to JSR. Many of them use a parameter block, that's one set, and many just check or modify an OS feature, and that's another. Both cases use A to determine what the function is. Then there are the I/O calls, and the CLI call. So, the total number of addresses to call is much lower than the number of facilities in the OS.


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 5:31 pm 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1382
Hmmm, sounds like you're trying to mimic the software interrupts ala x86, as the original (IBM) PC used INT 13h for Disk BIOS and INT 21h for DOS based calls. Is this correct?

My recently posted Mini BIOS for the 65C02 supports the 65C51 and 65C22 chips and checks for the 65C51 first followed by the 65C22 (both timers) and is quite efficient. It's also soft vectored which allows replacing or inserting additional service routines pre or post. Adding hardware as Ed suggested to leverage the VPB line would be more involved.

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 5:36 pm 
Offline
User avatar

Joined: Sat Aug 22, 2015 8:54 pm
Posts: 31
Another thing I want to do is have a changeable "vector table" sorta like the IVT in x86, this way the user can set up their own calls. (This is a BIOS, so anyone can write an OS and have it work under this BIOS.) Edit: floobydust, that is exactly what I am trying to do. I am basing my machine on the NMOS 6502, however, and the 65C02 has additional instructions not supported by the 6502, if I am not mistaken.

I wonder if I need to follow BRK up with a NOP in DASM? I have yet to find DASM manuals, etc. that say anything about it so I wonder if I need to do so here?

_________________
It may look hard, but it won't take long if you take it one byte at a time.


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 6:36 pm 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1382
Well, you can use the BRK instruction to force the CPU to jump to the IRQ vector, then save the registers and determine if a hardware interrupt or a software BRK instruction generated it. The Commodore Vic-20 and C64 did this (as does my BIOS). As BRK is a two byte instruction, you can use the second byte as an index or function call number, which really isn't what you want. Your other option is to use the A register for a call number and the X/Y registers to hold some additional parameters and have the BRK service routine call a routine based on a table indexed by the A register call number.

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 7:08 pm 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
You could add an external interrupt controller that presents the highest priority interrupt vector at a special location. Handling the interrupt would be a matter of jmp (vector). That way you don't have to modify the core.


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 7:49 pm 
Offline
User avatar

Joined: Sun Oct 13, 2013 2:58 pm
Posts: 491
Location: Switzerland
You could also use a simple priority encoder (74LS148). This also solves the problem that W65C22 IRQ outputs are push-pull outputs. Make sure that you use a latch to provide a stable output to the 6502. As you need a tri-state buffer between the 74LS148 and the data bus. For example a 74AC573 could be used. When you have a W65C816 you already have a inverted PHI2 signal for the bank address latch. And if you want you can use VPB together with A0 to disable the upper memory and select the output of the latch when the low byte of the vector is read. I would avoid to read both bytes of the vector from an interrupt priority logic as you have no guarantee that during both bytes the same interrupt has the highest priority. This should even work for systems with a high CPU clock (aka 14MHz and more).


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 9:11 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
Using the BRK signature byte doesn't seem to me to be a huge hassle, although I've never tried it in a real-world application. The NMOS service code would look something like this (please alert me in the somewhat likely event that I made an off-the-cuff coding error or two):
Code:
common_irq_entry:
        pha                     ; push a, x and y
        txa
        pha
        tya
        pha
        tsx
        lda  $0104,x            ; check stacked p to see if
        and  #brk_bit           ;   we were called by a brk #
        bne  brk_handler        ;   or a hardware IRQ
irq_handler:
        ...                     ; handle hardware IRQ
        ...                     ;   as appropriate
common_irq_exit:
        pla
        tay
        pla
        tax
        pla
        rti
brk_handler:
        lda  $0105,x
        sta  zp_temp
        ldy  $0106,x
        dey
        sty  zp_temp+1
        ldy  #$ff
        lda  (zp_temp),y        ; get brk signature byte
        tay
        lda  brk_vec_h,y        ; load and push appropriate
        pha                     ;   service routine address
        lda  brk_vec_l,y        ;   from split vector table
        pha
        php
        rti                     ; jump to service routine
brk_00:
        ...                     ; service a brk #$00 instruction
        ...
        jmp  common_irq_exit
brk_01:
        ...                     ; service a brk #$01 instruction
        ...
        jmp  common_irq_exit
brk_02:
        ...                     ; service a brk #$02 instruction
        ...
        jmp  common_irq_exit
brk_03:
        ...                     ; service a brk #$03 instruction
        ...
        jmp  common_irq_exit
brk_vec_l:
        dcb  <brk_00,<brk_01,<brk_02,<brk_03 ; ,...
brk_vec_h:
        dcb  >brk_00,>brk_01,>brk_02,>brk_03 ; ,...


The 'c02 version of the same would be a bit shorter and more efficient, and the '802/'816 version of the same even more so.

Mike B.

[Edits: a few minor code revisions, including BDD's proposal to save a cycle or maybe two per hardware IRQ.
]


Last edited by barrym95838 on Mon Aug 24, 2015 3:43 am, edited 5 times in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 9:28 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8390
Location: Midwestern USA
I cover the use of software interrupts for calling kernel services in my 65C816 interrupts article (see here). The principles are mostly applicable to the 65C02.

Incidentally, the INT XXh instruction in the x86, like the TRAP $XX instruction in the MC68000, is a "vectored interrupt" that is vaguely analogous to the 65xx BRK instruction followed by a defined signature byte. You could use a macro such as INT x, where x is the interrupt number, to simulate the x86 equivalent. The macro would expand to:

Code:
         brk
         .byte x

However, as Ed noted and is evident with Mike's code, fetching the signature byte is a somewhat convoluted process. It should be noted that UNIX, which uses software interrupts to call kernel APIs, doesn't use the interrupt number at all—for example, such a call on the 68000 is TRAP $00. Instead, a general purpose register—usually D0 on the 68000—is loaded with an API index number and then the software interrupt is executed. This is practical because any parameters required by the API are passed via a stack frame that is pushed prior to the actual API call.

With the 65C816, the COP instruction can be used for the purpose. Here again, some gymnastics are needed to retrieve COP's signature, an exercise that is further complicated by the bank-oriented nature of the '816 (I describe that in my 65C816 interrupts article). I recommend COP $00 for calling kernel or BIOS functions, as it's easier to model the UNIX API method by using the 65C816's stack instructions, e.g., PEA and PEI, to set up a stack frame.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Last edited by BigDumbDinosaur on Sun Aug 23, 2015 10:43 pm, edited 2 times in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 9:54 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8390
Location: Midwestern USA
barrym95838 wrote:
Code:
common_irq_entry:
        pha                     ; push a, x and y
        txa
        pha
        tya
        pha
        tsx
        lda  $0104,x            ; check stacked p to see if
        and  #brk_bit           ; we were called by a brk #
        beq  irq_handler        ; or a hardware IRQ
brk_handler: ...etc...

The above would be more efficient as:

Code:
common_irq_entry:
        pha                     ; push a, x and y
        txa
        pha
        tya
        pha
        tsx
        lda  $0104,x            ; check stacked SR to see if
        and  #brk_bit           ; we were called by a brk #
        bne  brk_handler        ; or a hardware IRQ
;
irq_handler ...etc...
;
brk_handler: ...etc...

Interrupt service routines should always be arranged so that branches are not taken unless absolutely necessary. In the above case, it's a safe bet that the ISR will be called far more often to service a hardware interrupt than a software interrupt. Hence having the code fall through if the interrupt is an IRQ saves the expense of taking the branch. That could become quite important during, say, processing a long stream of output characters to a UART.

Code:
brk_handler:
        lda  $0105,x
        sta  zp_temp
        lda  $0106,x
        sta  zp_temp+1
        dec  zp_temp+1
        ldy  #$ff
        lda  (zp_temp),y        ; get brk signature byte
        tay
        lda  brk_vec_h,y        ; load and push appropriate
        pha                     ;   handler address (-1)
        lda  brk_vec_l,y        ;   from split vector table
        pha
        rts                     ; jump to handler

That ought to work.

Clearly, getting the signature is cumbersome and, surprisingly, is about as cumbersome on the 65C816 due to the fact that the load operation is from the bank defined in DB, which won't necessarily be the bank in which the interrupted program is running. Hence the service routine's front end has to load the PB value from the stack into DB, which isn't quite as simple as it sounds. In my opinion, it's less trouble to load a register with an API index and ignore the signature byte. The resulting code is more succinct.

Incidentally, following long practice (back to the latter 1970s), I refer to the status register as SR, as every machine language monitor I have used does so. In the 65C816, DB is the data bank, DP is the direct page pointer, PB is the program bank, PC is the program counter and SP is the stack pointer.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple interrupts?
PostPosted: Sun Aug 23, 2015 10:08 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
Thanks for looking over that for me, BDD. I edited it after you copied it, changing the rts to php;rti to make the jump table a bit clearer ... a small matter of taste and possibly of future maintainability, but at the expense of one code byte and a few machine cycles. I don't worry about cycle counts until it becomes obvious that I should. In your proposed revision, you are saving at most two machine cycles ... a triviality in all but the most high-strung cases. For some reason, I do care about code footprint size, but that's probably just an idiosyncrasy of mine.

Less than 30 code bytes (plus an appropriately-trimmed jump table) and less than 60 cycles to add the brk signature byte functionality is certainly not a huge cost, but I understand that a different approach may be more desirable in some instances. It could be an overall space win in a larger operating system, due to the space saved in the callers, but the callers would need to have several dozen instances of brk # to make that come to be.

Mike B.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 81 posts ]  Go to page 1, 2, 3, 4, 5, 6  Next

All times are UTC


Who is online

Users browsing this forum: AndrewP and 8 guests


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: