6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Jun 07, 2024 8:04 am

All times are UTC




Post new topic Reply to topic  [ 21 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Kernel User Interrupt
PostPosted: Wed Apr 20, 2022 9:28 am 
Offline

Joined: Mon May 03, 2021 12:07 am
Posts: 21
Location: Brisbane Australia
Hi all,

In my 6502 system I have kernel code located in the top 8k. (FLASH ROM)
The IRQ interrupt points to $0400 in RAM. 512 bytes has been allocated for future interrupt code, but could be extended.
The current interrupt code only uses 60 bytes.

The IRQ interrupt code is copied from ROM to RAM on start before interrupts are enabled.
The interrupt pseudo code looks something like this,

IF ACIA interrupt
process ACIA interrupt
IF 6522 timer interrupt
process 6522 timer interrupt
-- add USER interrupt here--

RTI

I load "USER" programs into RAM and run them from the kernel.
I want the USER programs to be able to make use of the 6522 timer interrupt.
What is the best way to create a "hook" into the kernel?

Here are my thoughts so far

add a JSR $0500 (in the pseudo code above)
add a RTS at $0500

Then, when the USER program runs it can replace the RTS at $0500 with actual interrupt code.
and when it finishes it can put back the RTS

Another thought was to place 3 NOP's at -- add USER interrupt here--
The USER program can simply replace them with a JSR to some code and then return the NOP's when finished.
The problem with this is finding the NOP's as the address moves if the kernel IRQ code is modified at a later date.
The USER program could scan for then assuming NOP's are not needed in the IRQ code.

Another possibility is to JSR to the RTI address + 1 and start the user code there.
This way the locations are not fixed and there is no unused memory between kernel interrupt and user interrupt code.
The USER program can scan from $0400 to the first RTI (there should only be one)

How did the C64 and other computers do this back in the day?

Any suggestions?

Thanks
Andre


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 20, 2022 9:49 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10822
Location: England
The indirect jump is rather a good tool here. JMP (somevector) allows you to put two bytes in a well-known location, and it will either do the usual thing (because the OS initialised those two bytes) or a new thing (because some program wrote its own handler address to those two bytes.)

It's good practice, just before writing your new value to the two-byte vector, to first read the value that's there and put it in your own exit routine, so your new code will fall through to the pre-existing code if it finds it has nothing to do, or if it's done with what it's doing.

Acorn's MOS actually chains IRQ handling through two vectors - an early one, which a user can use for very time-critical purposes, at the risk of delaying, say, ACIA handlings, and a late one, which comes after all the OS business, whereby the user can do things without adding latency to the OS.

See IRQ1V and IRQ2V in the MOS:
https://tobylobster.github.io/mos/mos/S-s11.html

Perhaps start at the explanation of vectors:
https://tobylobster.github.io/mos/mos/S-s2.html#SP13

Edit: for the C64's KERNAL, perhaps see here, and nearby
https://www.pagetable.com/c64ref/c64mem/#0314


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 20, 2022 10:31 am 
Offline

Joined: Fri Jul 09, 2021 10:12 pm
Posts: 741
BigEd wrote:
The indirect jump is rather a good tool here. JMP (somevector) allows you to put two bytes in a well-known location, and it will either do the usual thing (because the OS initialised those two bytes) or a new thing (because some program wrote its own handler address to those two bytes.)

It's good practice, just before writing your new value to the two-byte vector, to first read the value that's there and put it in your own exit routine, so your new code will fall through to the pre-existing code if it finds it has nothing to do, or if it's done with what it's doing.

Acorn's MOS actually chains IRQ handling through two vectors - an early one, which a user can use for very time-critical purposes, at the risk of delaying, say, ACIA handlings, and a late one, which comes after all the OS business, whereby the user can do things without adding latency to the OS.

This system works very well and it's very easy to add your own handler for a specific interrupt, the user code looks something like this:

Code:
    .myirq1v
        BIT &FE6D              \ Check for user VIA interrupt
        BVS handletimer1irq    \ Is it a timer 1 interrupt?
        JMP (savedirq1v)       \ Chain to the old irq1v handler

So it's very easy to intercept a specific interrupt and pass everything else to the OS - especially if you don't still need the OS to process the interrupt you're handling.

Acorn's MOS also has a third vector, in addition to the two BigEd mentioned above, called the "event vector", which is even easier for user code to deal with - alongside the OS's usual handling of interrupts, it also passes specific events through this vector. For example, one event is vsync, another is a 100Hz clock tick, another is a key being pressed. For each of these it sets the A register (I think) to a code that identifies the event type, and calls the user handler via the "eventv" vector. This means the user code can immediately process the events without having to interrogate hardware directly. See more notes here: https://tobylobster.github.io/mos/mos/S-s14.html#SP25

Finally, to reduce the latency when handling disc and network interrupts, these are connected to the NMI pin, and the NMI vector at FFFA points into RAM at 0D00, which is initialised with RTI on startup. The disc and network software copy various NMI handlers to that RAM location to deal with different types of events (e.g. reading from disc and writing to disc use different handlers). So it is possible to use this sort of system, but it has to be carefully managed.


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 20, 2022 4:01 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8199
Location: Midwestern USA
I have indirect vectors in the firmware of my POC units. An interrupt handler does some basic front end work and then jumps through the relevant vector, which normally returns right back to the interrupt handler. If I wish to add to or replace an interrupt handler I load the new code into RAM and then change the vector to point to my new code.

There were several software projects in which I “wedged” code into the interrupt subsystem in this fashion. It’s how I developed my interrupt-driven SCSI driver (lots of opportunities for crashes with that one) without having to constantly change the firmware.

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


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 21, 2022 1:57 am 
Offline

Joined: Mon May 03, 2021 12:07 am
Posts: 21
Location: Brisbane Australia
Thanks,

I get the idea of the JMP (indirect). Looks like the way to go.

What I don't quite see from all the BBC code is how one returns from the JMP to continue the original IRQ code.

Let's say I have a vector table looking something like this,

vector 1 at $200
vector 2 at $202
vector 3 at $204

simplified irq pseudo code

irq entry

kernel does something

JMP ($200 contains address of label_vec1)
label_vec1:
JMP ($202 contains address of label_vec2)
label_vec2:
JMP ($204 contains address of label_vec3)
label_vec3:

RTI

The above code "falls through" to the RTI as the JMP's jump to the next address.

Now, in my USER program I modify address $200 (address of my code) to point to my irq code.
upon completion of my code I can simply JMP ($202)
if no other vectors have been modified then the code falls through to the RTI

However if all 3 were modified then I need a 4th vector in my table to act as a return address.
This 4th one is never modified.

Is this the way it should work or am I still missing something?

Regards
Andre


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 21, 2022 2:14 am 
Offline

Joined: Fri Jul 09, 2021 10:12 pm
Posts: 741
Andre wrote:
What I don't quite see from all the BBC code is how one returns from the JMP to continue the original IRQ code.

The user code is responsible for capturing the original value of the vector before overwriting it, and chaining to that address when it finishes.

e.g.
Code:
init:
    PHP
    SEI
    LDA $200
    STA mysavedvector200
    LDA $201
    STA mysavedvector200+1
    LDA #<my200handler
    STA $200
    LDA #>my200handler
    STA $201
    PLP
    RTS

my200handler:
    ; ... code here ...
    JMP (mysavedvector200)

So it ends with the same indirect jump the OS was using, but through its own copy of the old value of the vector.

Quote:
irq entry

kernel does something

JMP ($200 contains address of label_vec1)
label_vec1:
JMP ($202 contains address of label_vec2)
label_vec2:
JMP ($204 contains address of label_vec3)
label_vec3:

RTI

That's not how Acorn's code looks - the routine that gets called is usually not immediately following the JMP statement - but you could write it that way if you like, of course, and the flow would make sense. There's probably not much point in calling several indirect jumps in a row like that though.

In Acorn's code, the default IRQ1V handler polls all the hardware for interrupts that it knows how to handle (e.g. the serial subsystem, one of the VIAs, the ADC port), proxies some of them to events via the EVENTV, and finally if the interrupt wasn't from a hardware source that IRQ1V is aware of, it offers the interrupt to various paged ROMs before ultimately calling IRQ2V if nobody knows what to do with it. So these vectors aren't called back-to-back, there's a lot of code in between, and in fact it's more like they're chained together. If a user IRQ1V handler just RTIs instead of chaining to the system handler, the system won't see the interrupt at all, IRQ2V won't get called, etc.


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 21, 2022 3:16 am 
Offline

Joined: Mon May 03, 2021 12:07 am
Posts: 21
Location: Brisbane Australia
Hi

I understand that the user program will capture an replace the vectors in the table as required.

Apologies, bad example.
3 successive JMP's is not what I am looking for, but it does serve to illustrate the problem / solution.

I would currently only require 2 vectors as follows
1. Every time the 6522 timer interrupt occurs
2. Every time the irq occurs, but after the kernel has done it's thing.


irq entry
if ACIA has new data then process it
if 6522 timer interrupt then process it AND JMP(vector1)

;end of current kernel code
JMP(vector2)
RTI

Back to my question, after the JMP(vector2), how does one return to complete the RTI?
I could of course ignore the RTI and simply add the RTI at the end of my irq code, but that creates an exception.
Some user irq code requires the RTI and some do not.

Is a vector3 holding the address of the RTI the way to go or is there a better way?

Regards
Andre


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 21, 2022 3:43 am 
Offline

Joined: Fri Jul 09, 2021 10:12 pm
Posts: 741
Andre wrote:
Back to my question, after the JMP(vector2), how does one return to complete the RTI?
I could of course ignore the RTI and simply add the RTI at the end of my irq code, but that creates an exception.
Some user irq code requires the RTI and some do not.

Is a vector3 holding the address of the RTI the way to go or is there a better way?

Assuming you meant "vector2" there, then yes that would work, with the user code chaining to the default vector2 which just points to an RTI.

Another option that may be better, especially for vector1, would be for your interrupt handler to JSR to the indirect JMP instruction, so that the user code just has to RTS when it's finished. It no longer allows the user code to prevent the OS from running (by not chaining) and doesn't allow multiple different bits of user code to all hook the event, but it's better in some situations.


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 21, 2022 3:58 am 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1373
There's multiple ways to handle entries and returns to and from the interrupt handler(s). For my systems, I use a set of 8 vectors located in page $03, which are assigned as follows:

Code:
VEC_TABLE      ;Vector table data for default ROM handlers
;
                .DW     NMI_VECTOR      ;NMI Location in ROM
                .DW     BRKINSTR0       ;BRK Location in ROM
                .DW     INTERUPT0       ;IRQ Location in ROM
;
                .DW     M_WARM_MON      ;NMI return handler in ROM
                .DW     M_WARM_MON      ;BRK return handler in ROM
                .DW     IRQ_EXIT0       ;IRQ return handler in ROM
;
                .DW     M_COLD_MON      ;Monitor Cold start
                .DW     M_WARM_MON      ;Monitor Warm start


The above addresses get copied to Page $03 during startup. There are 8 free vectors which can be used to extend any of the handlers (IRQ, NMI or BRK). The code below shows saving and restoring registers pre/post the handler routine and the indirect jumps to Page $03 to the actual interrupt handler.

Code:
;
IRQ_VECTOR                              ;This is the ROM start for the BRK/IRQ handler
                PHA                     ;Save A Reg (3)
                PHX                     ;Save X Reg (3)
                PHY                     ;Save Y Reg (3)
                TSX                     ;Get Stack pointer (2)
                LDA     $0100+4,X       ;Get Status Register (4)
                AND     #$10            ;Mask for BRK bit set (2)
                BNE     DO_BRK          ;If set, handle BRK (2/3)
                JMP     (IRQVEC0)       ;Jump to Soft vectored IRQ Handler (6)
DO_BRK          JMP     (BRKVEC0)       ;Jump to Soft vectored BRK Handler (6)
NMI_ROM         JMP     (NMIVEC0)       ;Jump to Soft vectored NMI handler (6)
;
;This is the standard return for the IRQ/BRK handler routines
;
IRQ_EXIT0       PLY                     ;Restore Y Reg (4)
                PLX                     ;Restore X Reg (4)
                PLA                     ;Restore A Reg (4)
                RTI                     ;Return from IRQ/BRK routine (6)
;


As you can see, each interrupt function (NMI, IRQ and BRK) has a vector that points to the actual handler, and, a vector that is used when that routine exits. By having 8 additional soft vectors that are free, I can add to any of the three routines and insert a new routine pre or post to the main routine. An example is detecting an IDE device. If my BIOS finds an IDE device, it inserts an interrupt handler into the chain. It also inserts the IDE handler to run before the existing handler, which by default supports a single or dual UART with a timer.

The NMI routine for my BIOS basically resets only the console UART and re-initializes the base vectors and then does a warm start of the monitor code. Note that I also have soft vectors for the Monitor warm and cold start entries, so these can be changed as well if needed. Hope this helps....

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


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 21, 2022 4:36 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8199
Location: Midwestern USA
Andre wrote:
I get the idea of the JMP (indirect). Looks like the way to go.

What I don't quite see from all the BBC code is how one returns from the JMP to continue the original IRQ code.

Some example code for you that “wedges” into two different interrupt systems. This is for the 65C816, which has the COP software interrupt. That said, the principle is the same for all members of the 6502 family.

Code:
;===============================================================================
;
;wedge: WEDGE PATCHES INTO INTERRUPT SYSTEM
;
wedge    rep #m_setr           ;16-bit registers
         ldx !#hwstack         ;set top of...
         txs                   ;hardware stack
         lda ivcop             ;current COP jump vector
         cmp !#newcop          ;already wedged?
         beq .0000010          ;yes, skip
;
         sta ivspareb          ;save old COP vector
         lda !#newcop          ;set...
         sta ivcop             ;new COP vector
;
.0000010 lda ivirq             ;current IRQ jump vector
         cmp !#newirq          ;already wedged?
         beq .0000020          ;yes, skip
;
         sta ivsparea          ;save old IRQ vector
         lda !#newirq          ;set...
         sta ivirq             ;new IRQ vector
;
.0000020 lda !#0               ;zero all registers
         tax
         txy
         sep #m_setr           ;8-bit registers
         clc
         brk

Note that with the 65C816, 16-bit reads and writes are atomic operations, which is why I don't temporarily disable IRQs while changing the IRQ indirect vector. With the 8-bit 65C02, you have to disable IRQs while changing the vector. Otherwise, it’s possible to have an IRQ hit when only one of the two vector address bytes have been altered, likely sending the MPU out into the asteroid belt.

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


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 21, 2022 7:40 am 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1433
Location: Scotland
Andre wrote:
Back to my question, after the JMP(vector2), how does one return to complete the RTI?
I could of course ignore the RTI and simply add the RTI at the end of my irq code, but that creates an exception.
Some user irq code requires the RTI and some do not.


Typically you create a "chain".

So you read and save the existing pointer, replace it with your own, and at the end of your own code you JMP (savedPointer).

That way many different routines can insert themselves into this chain. (and remove at a later date, if required)

This way is used in other vectors too - e.g. the Apple II charin/charout vectors and the dozen or so more for the BBC Micro operating system for things like file operations as well as character IO.

-Gordon

_________________
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 21, 2022 7:53 am 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1433
Location: Scotland
drogon wrote:
Andre wrote:
Back to my question, after the JMP(vector2), how does one return to complete the RTI?
I could of course ignore the RTI and simply add the RTI at the end of my irq code, but that creates an exception.
Some user irq code requires the RTI and some do not.


Typically you create a "chain".


following this up with an example from my RubyOS which is an Acorn MOS compatible OS - this is a small section of the vectors that live in the system from $FF80 upwards:

Code:
; Relocated original Acorn MOS entrypoints.

__osRdch:       jmp     _osRdch         ; FF85: (&0210)  Get a byte from current input stream
__osAsci:       cmp     #$0D            ; FF88:          Output a byte to VDU stream expanding
                bne     __osWrch        ;                        carriage returns (&0D) to LF/CR (&0A,&0D
)
__osNewl:       lda     #$0A            ; FF8C:          Output LF/CR to VDU stream
                jsr     __osWrch        ; FF8E:          Output A followed by CR to VDU stream
__osWrcr:       lda     #$0D            ; FF91:          Output a CR to VDU stream
__osWrch:       jmp     _osWrch         ; FF93: (&020E)  output a character to the VDU stream
__osWord:       jmp     _osWord         ; FF96: (&020C)  Perform operation using parameter table
__osByte:       jmp     _osByte         ; FF99: (&020A)  Perform operation with single bytes
__osCli:        jmp     _osCli          ; FF9C: (&0208)  Pass string to command line interpreter


So any program that needs to print characters would typically call _osWrch which is $FF93, then the 'real' code is:

Code:
_osWrch:
        jmp     (wrchV)
_nvWrch:                        ; Non-vectored osWrch - always write to the host


At system boot time the vector (wrchV) is initialised from a table - which looks like:

Code:
;*********************************************************************************
; indirectionVectors:
;       These are the indirection vectors copied to page $02.
;*********************************************************************************

indVectors      = $0200

indVectorsStart:
userV:          .word   _userV
brkV:           .word   _brkV
irq1V:          .word   _irq1V
irq2V:          .word   _irq2V
cliV:           .word   _nvCli
byteV:          .word   _nvByte
wordV:          .word   _nvWord
osWrchV:        .word   _nvWrch
osRdchV:        .word   _nvRdch

fileV:          .word   _nvFile
argsV:          .word   _nvArgs

bgetV:          .word   _nvBget
bputV:          .word   _nvBput
gbpbV:          .word   _nvGbpb
findV:          .word   _nvFind
fscV:           .word   _fscV
evntV:          .word   _evntV
uptV:           .word   _uptV
netV:           .word   _netV
vduV:           .word   _vduV
keyV:           .word   _keyV
insV:           .word   _insV
remV:           .word   _remV
cnpV:           .word   _cnpV
ind1V:          .word   _ind1V
ind2V:          .word   _ind2V
ind3V:          .word   _ind3V


There's a lot there, but the OS on the BBC Micro lets you take over virtually everything in a well supported and documented manner.

IRQ is similar.

These do add a few more cycles to each routine but as it was OK on a 2Mhz BBC Micro in 1981, I think it's fine on my 16Mhz Ruby board today.


-Gordon

_________________
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/


Top
 Profile  
Reply with quote  
PostPosted: Fri Apr 22, 2022 5:39 am 
Offline

Joined: Mon May 03, 2021 12:07 am
Posts: 21
Location: Brisbane Australia
Thanks for all the help everyone,

Gordon, I see that you are stating with

jsr __osWrch

This takes care of the return (via RTS) so that the original code continues from where the JSR occurred.
The function that you are calling takes care of the JMP (indirect)

_osWrch:
jmp (wrchV)

and wrchV is the table entry for the function you want.
This makes sense.

For others interested in this topic I will present what I have been doing in more detail. Perhaps it helps someone down the track.

I am actually coding 80% c (CC65) and 20% assembler which makes things a little more interesting.
I have some kernel code / boot loader that allows me to upload programs via the terminal.
It also provides other system services.

My interrupt code, in assembler, is copied to RAM on power up. It needs to run from RAM as I can bank switch the upper 8K kernel code.
The flash ROM device is 512K, so 64 x 8k blocks. (Each ROM bank must duplicate the interrupt vectors)

My vector table (at this stage) looks like this

vector1 $04F0 10ms timer interrupt
vector2 $04F2 1 second timer interrupt
vector3 $04F4 IRQ interrupt after kernel code has completed

Note that my irq code is located in the top 8K. In order to generate vectors that will work at a lower address the vector addresses need to be recalculated.

The kernel generates default vectors as follows, (inline assembler in c kernel code)

__asm__ ("LDA #<(_vector1_return - _irq + $0400)");
__asm__ ("STA $04F0");
__asm__ ("LDA #>(_vector1_return - _irq + $0400)");
__asm__ ("STA $04F1");

where irq is the start of the _irq code and _vector1_return is a label at the return address
The irq code looks like this

...yes it was a 10ms timer interrupt
JMP ($04F0) ; jump to vector1 interrupt
_vector1_return:
INC SYSTEM_TIMER
CLC
LDA SYSTEM_TIMER
CMP #$64 ; HAS TIMER REACHED 100 (0x64)
BCC _exit_irq
LDA #$00
STA SYSTEM_TIMER
INC SYSTEM_SECONDS

JMP ($04F2) ; jump to vector2 interrupt
_vector2_return:
_not_via:

JMP ($04F4) ; jump to vector3 interrupt
_vector3_return:
_exit_irq:
PLA
PLX
PLY

_irq_last_byte_address:
RTI ; Return from IRQ interrupt


In the USER program I save the old IRQ vector upon entry,

unsigned int old_irq_address;
*((unsigned char *)&old_irq_address) = *(unsigned char *) 0x04F0; // save the low byte of vector1
*((unsigned char *)&old_irq_address+1) = *(unsigned char *) 0x04F1; // save the high byte of vector1

and replace it with the address of my new code/function.

SEI(); // disable interrupts
__asm__ ("LDA #<_USER_Program_Timer_Interrupt");
__asm__ ("STA $04F0");
__asm__ ("LDA #>_USER_Program_Timer_Interrupt");
__asm__ ("STA $04F1");
CLI(); // enable interrupts

In this example _USER_Program_Timer_Interrupt is a c function as follows,(probably won't be in future)

void USER_Program_Timer_Interrupt(void)
{
temp_counter++;
__asm__ ("JMP (_old_irq_address)");
}

and when the USER program is finished I restore the old irq vector.

SEI(); // disable interrupts
*(unsigned char *) 0x04F0 = *((unsigned char *)&old_irq_address); // restore the low byte of vector1
*(unsigned char *) 0x04F1 = *((unsigned char *)&old_irq_address+1); // restore the high byte of vector1
CLI(); // enable interrupts

That's pretty much it.

Reagrds
Andre


Top
 Profile  
Reply with quote  
PostPosted: Fri Apr 22, 2022 6:02 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10822
Location: England
Thanks - it's much clearer, to anyone I think, when you show
- the labels
- the vector initialisation

I think there's something you might still be missing. Your vector2 and vector3 serve pretty much the same purpose, in terms of what happens before them and after them. Why two vectors? The point of the new handlers inserting themselves into a chain is that you can have any number of new handlers, from zero to many, without needing any pre-allocated series of vectors.

It should also be clear, although it's not important, that a JMP is a JMP, and it doesn't matter what code or data follow it. In your case, for convenience or by convention perhaps, you've got the intended subsequent code right after the JMP. There's nothing wrong with that, but I wonder if it says something about the way you're thinking about these mechanisms.
(Acorn's system has two vectors because one is early and the other is late - they have two different purposes.)


Top
 Profile  
Reply with quote  
PostPosted: Fri Apr 22, 2022 6:21 am 
Offline

Joined: Mon May 03, 2021 12:07 am
Posts: 21
Location: Brisbane Australia
Hi

Yes, the vector arrangement looks a little odd. That's why the previous (poor) example showed 3 vectors in a row, which makes no sense out of context.
But this arrangement does make sense.

Vector1 triggers if a 10ms event has occurred
Vector2 triggers if a 1 second event has occurred
Vector3 triggers every time an irq interrupt occurs

Vectror3 is effectively ALWAYS triggered

It is possible for all 3 vectors to trigger in one pass through the irq interrupt.

So a user program can

do something every 10ms in vector1
do something every second in vector2
handle a new device not recognized by the kernel in vector3

It is just a coincidence that vector2 happens to follow vector3 in the sequence of the code.
As new code is added the gap between vector2 and vector3 may be filled.

Hope that makes sense.

Regards
Andre


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

All times are UTC


Who is online

Users browsing this forum: No registered users and 15 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: