        TTL     => NewIRQs

; *****************************************************************************
;
; Main IRQ routine:
;     Push  workregs,lr
;     IRQsema -> TOS
;     stack -> IRQsema
;     call IRQ1V
;     IRQs off
;     TOS ->IRQsema
;     process callback, pulling workregs,pc at some point
;
; *****************************************************************************

Initial_IRQ_Code ROUT

        SUB      lr, lr, #4
        Push     "r0-r3, r11, r12, lr"
; ** For God's sake remember to change the heap manager if you change this
; ** register list!!!!!!! And the [sp_irq, #4*6] below
 [ IRQSTK - 7*4 :AND: 15 <> 0
 ! 0,"IRQ STM/LDM making extra S cycle into N"
 ]

        MOV     r12, #0
        LDR     r0, [r12, #IRQsema]
        Push    r0
        STR     sp_irq, [r12, #IRQsema]
        MOV     lr, pc
        LDR     pc, [r12, #IRQ1V]

; IRQ1V called with r0-r3,r11,r12 trashable. r12=0

; Stu has a theory that 1N cycle can be saved by the default IRQ1V pointing
; at a location containing a branch to our code; we then do something like
;  LDR R0, [R12, #IRQ1V]
;  CMP R0, #OurIRQ1V
;  BNE somebastardsonIRQ1V
;  .... fall into default IRQ1V code

        MOV      r11, #0
        Pull     r0
        STR      r0, [r11, #IRQsema]

        LDRB     r11, [r11, #CallBack_Flag]
        CMP      r11, #0
        Pull     "r0-r3, r11, r12, pc", EQ, ^

        TST      r11, #CBack_Postpone
        LDREQ    lr, [sp_irq, #4*6]
        TSTEQ    lr, #SVC_mode :OR: I_bit
        Pull     "r0-r3, r11, r12, pc", NE, ^

; Do a CallBack: asked for, not postponed, and we're returning into user mode.

        Pull     "r0-r3, r11, r12"
        TEQP     pc, #SVC_mode
        MOVNV    r0, r0
        Push     "r10-r12"
        TEQP     pc, #IRQ_mode
        MOVNV    r0, r0
        Pull     "r12"                  ; lr really
        TEQP     pc, #SVC_mode
        MOVNV    r0, r0
        MOV      lr, r12
        MOV      r10, #0
        LDRB     r11, [r10, #CallBack_Flag]
        B        Do_CallBack

        LTORG

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Default IRQ1V: despatch on interrupting device

; Now copied to RAM, together with vector entries and device tables
                                                                               
DefaultIRQ1Vcode ROUT

        MOV     r3, #IOC
        LDRB    r0, [r3, #IOCIRQREQB]
        CMP     r0, #0
        LDREQB  r0, [r3, #IOCIRQREQA]
      
        ADREQ   r1, IrqReqADevnos
        ADRNE   r1, IrqReqBDevnos
        LDRB    r0, [r1, r0]            ; pick up offset in device despatcher

        ADD     r1, pc, r0, LSL #2      ; so table contains DevNo * 3
        LDMIA   r1, {r12, pc}


; ******* IRQ device handlers entered with r0-r3,r11,r12,r14 trashable *******
;   r3  -> IOC
;   r12 =  what they asked for
;   r14 =  return address to MOS IRQ exit sequence

        GET     &.Hdr.DevNos

NoInterrupt * 16 ; internal devno; when ReqA = 0!

Devices

; Register A devices
; pbusy handler
        & 0             ; R12 value
        & IRQ           ; call address
        & 0             ; link
; ringing handler
        & 0
        & IRQ
        & 0
; printer acknowledge
        & OsbyteVars
        & PrinterIRQ
        & 0
; vsync handler
        & OsbyteVars
        & VsyncIRQ
        & 0
; power on reset: this can't happen, but call IRQ2V if it does.
        & 0
        & IRQ
        & 0
; timer0
        & OsbyteVars
        & TickOne
        & 0
; timer1
        & 0
        & IRQ
        & 0
; FIQ downgrade
        & 0
        & IRQ
        & 0

; register B devices
; PFIQ downgrade
        & PFIQasIRQ_Chain - (PodDesp_Link-PodDesp_R12Val)
        & PFIQasIRQ_Despatch
        & 0
; sound
        & 0
        & IRQ
        & 0
; serial
        & OsbyteVars
        & RS423IRQ
        & 0
; winnie IRQ
        & 0
        & IRQ
        & 0
; Disc changed
        & 0
        & IRQ
        & 0
; podule IRQ
        & PIRQ_Chain - (PodDesp_Link-PodDesp_R12Val)
        & PIRQ_Despatch
        & 0
; serial TX
        & IOC
        & IrqTx
        & 0
; serial RX
        & IOC
        & IrqRx
        & 0

; Neither A or B is interrupting, which is impossible: just call IRQ2V anyway
        & 0
        & IRQ
        & 0


; Following tables encode the priority of the devices within each register

       GBLA  DTabC
DTabC  SETA 1

IrqReqADevnos
  =  NoInterrupt*3
  WHILE DTabC <256
  [ (DTabC:AND:force_bit)<>0
  = FIQDowngrade_DevNo*3
  |
  [ (DTabC:AND:timer0_bit)<>0
  = Timer0_DevNo*3
  |
  [ (DTabC:AND:vsync_bit)<>0
  = VSync_DevNo*3
  |
  [ (DTabC:AND:timer1_bit)<>0
  = Timer1_DevNo*3
  |
  [ (DTabC:AND:pack_bit)<>0
  = PrinterAck_DevNo*3
  |
  [ (DTabC:AND:pbusy_bit)<>0
  = PrinterBusy_DevNo*3
  |
  [ (DTabC:AND:ring_bit)<>0
  = Ringing_DevNo*3
  |
  [ (DTabC:AND:por_bit)<>0
  = PowerOn_DevNo*3
  ]
  ]
  ]
  ]
  ]
  ]
  ]
  ]
DTabC SETA DTabC+1
  WEND


DTabC  SETA 1

IrqReqBDevnos
  =  NoInterrupt*3
  [ :LNOT: NewClockChip

  WHILE DTabC <256
  [ (DTabC:AND:serial_bit)<>0
  = Serial_DevNo*3
  |
  [ (DTabC:AND:(winnie_IRQ_bit:OR:winnie_DRQ_bit))<>0
  = WinnieIRQ_DevNo*3
  |
  [ (DTabC:AND:podule_IRQ_bit)<>0
  = Podule_DevNo*3
  |
  [ (DTabC:AND:serial_Rx_bit)<>0
  = SerialRx_DevNo*3
  |
  [ (DTabC:AND:serial_Tx_bit)<>0
  = SerialTx_DevNo*3
  |
  [ (DTabC:AND:1)<>0       ; this is apparently PFIQ downgrade
  = PFIQasIRQ_DevNo*3
  |
  [ (DTabC:AND:sound_IRQ_bit)<>0
  = Sound_DevNo*3
  ]
  ]
  ]
  ]
  ]
  ]
  ]
DTabC SETA DTabC+1
  WEND

 |

  WHILE DTabC <256
  [ (DTabC:AND:serial_bit)<>0
  = Serial_DevNo*3
  |
  [ (DTabC:AND:winnie_IRQ_bit)<>0
  = WinnieIRQ_DevNo*3
  |
  [ (DTabC:AND:podule_IRQ_bit)<>0
  = Podule_DevNo*3
  |
  [ (DTabC:AND:serial_Rx_bit)<>0
  = SerialRx_DevNo*3
  |
  [ (DTabC:AND:serial_Tx_bit)<>0
  = SerialTx_DevNo*3
  |
  [ (DTabC:AND:1)<>0       ; this is apparently PFIQ downgrade
  = PFIQasIRQ_DevNo*3
  |
  [ (DTabC:AND:sound_IRQ_bit)<>0
  = Sound_DevNo*3
  |
  [ (DTabC:AND:winnie_DRQ_bit)<>0  ; Disc changed on A1 etc.
  = DiscChanged_DevNo*3
  ]
  ]
  ]
  ]
  ]
  ]
  ]
  ]
DTabC SETA DTabC+1
  WEND

 ]

DefaultIRQ1Vcode_end

  ASSERT DefaultIRQ1Vcode_end - DefaultIRQ1Vcode = DefIRQ1Vspace

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Specialist despatchers for podules

                  ^  0
PodDesp_Address   #  4     ; address of IRQ status byte
PodDesp_Mask      #  4     ; for use on above
PodDesp_R12Val    #  4
PodDesp_CallAddr  #  4     ; address to call if ?Address AND Mask <> 0
PodDesp_Link      #  4     ; next node
PodDesp_NodeSize  #  0

 [ True

; In    r12 =    PFIQasIRQ_Chain - (PodDesp_Link-PodDesp_R12Val)
;             or PIRQ_Chain - (PodDesp_Link-PodDesp_R12Val)     from despatcher

PFIQasIRQ_Despatch ROUT

PIRQ_Despatch ; All the same thing now

 |

PFIQasIRQ_Despatch  ROUT

        LDR     r12, =PFIQasIRQ_Chain - (PodDesp_Link-PodDesp_R12Val)
        B       %FT01

PIRQ_Despatch ; NOROUT

        LDR     r12, =PIRQ_Chain - (PodDesp_Link-PodDesp_R12Val)
 ]


01      LDR     r12, [r12, #PodDesp_Link-PodDesp_R12Val]
        LDMIA   r12!, {r1, r2}           ; address and mask
        LDRB    r0, [r1]
        ANDS    r0, r0, r2
        BEQ     %BT01
        LDMIA   r12, {r12, pc}


Default_PIRQHandler_Node
Default_PFIQasIRQHandler_Node
        &       .+4                     ; address we know has non-zero value!
        &       -1                      ; mask
        &       0                       ; handler r12
        &       IRQ                     ; handler code
        &       0                       ; null link for naff release checking

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Claim of device vectors

; r0 = Device number
; r1 = call address
; r2 = r12 value
; r0 = PFIQ|PIRQ devno -> r3 = interrupt location
;                         r4 = interrupt mask

DeviceVector_Claim ROUT

        Push    "r0-r3, lr"

01      SWI     XOS_ReleaseDeviceVector ; Release until bored
        BVC     %BT01

        LDR     r0, [sp]
        CMP     r0, #NoInterrupt
        BGE     DV_Fail_NaffDevNo

        CMP     r0, #Podule_DevNo
        CMPNE   r0, #PFIQasIRQ_DevNo
        BEQ     PoduleChainClaim

        MOV     r3, #12
        BL      ClaimSysHeapNode
        BVS     DV_Exit
        LDR     r0, [sp]
        ADD     r0, r0, r0, LSL #1      ; *3
        LDR     r1, =DefaultIRQ1V-DefaultIRQ1Vcode+Devices
        ADD     r1, r1, r0, LSL #2
        TEQP    pc, #SVC_mode+I_bit     ; IRQs off for update
        LDMIA   r1, {r0, r3, r10}
        STMIA   r2, {r0, r3, r10}       ; copy current head into node
        LDR     r10, [sp, #4*2]         ; r12 value
        LDR     r11, [sp, #4*1]         ; call address
        MOV     r12, r2
        STMIA   r1, {r10-r12}           ; copy given info into head


DV_Exit STRVS   r0, [sp]                ; Common exit for both claim + release
        Pull    "r0-r3, lr"
        B       SLVK_TestV


DV_Fail_NaffDevNo
        ADR     r0, ErrorBlock_NaffDevNo
        SETV
        B       DV_Exit

        MakeErrorBlock NaffDevNo


PoduleChainClaim
        MOV     r3, #PodDesp_NodeSize
        BL      ClaimSysHeapNode
        BVS     DV_Exit
        MOV     r10, r2
        LDMFD   sp, {r0-r3}
        STR     r1, [r10, #PodDesp_CallAddr]
        STR     r2, [r10, #PodDesp_R12Val]
        STR     r3, [r10, #PodDesp_Address]
        STR     r4, [r10, #PodDesp_Mask]
        CMP     r0, #Podule_DevNo
        LDREQ   r0, =PIRQ_Chain
        LDRNE   r0, =PFIQasIRQ_Chain
        TEQP    pc, #SVC_mode+I_bit     ; IRQs off for update
        LDR     r1, [r0]
        STR     r1, [r10, #PodDesp_Link]
        STR     r10, [r0]
        B       DV_Exit

; .............................................................................
; Release of device vectors

; r0 = Device number
; r1 = call address
; r2 = r12 value
; r0 = PFIQ|PIRQ devno -> r3 = interrupt location (LDRB always used)
;                         r4 = interrupt mask

DeviceVector_Release ROUT

        Push    "r0-r3, lr"             ; Ensure same regset as above
        CMP     r0, #NoInterrupt
        BGE     DV_Fail_NaffDevNo

        TEQP    pc, #SVC_mode + I_bit   ; IRQs off while holding context
        CMP     r0, #Podule_DevNo
        CMPNE   r0, #PFIQasIRQ_DevNo
        BEQ     PoduleChainRelease

        ADD     r0, r0, r0, LSL #1      ; *3
        LDR     r12, =DefaultIRQ1V-DefaultIRQ1Vcode+Devices
        ADD     r12, r12, r0, LSL #2    ; address of node
        MOV     r11, #-1                ; "fudge" predecessor node

01      LDMIA   r12, {r3, r10}
        CMP     r3, r2
        CMPEQ   r10, r1
        BEQ     %FT02                   ; found it
        MOV     r11, r12
        LDR     r12, [r12, #8]          ; get the link
        CMP     r12, #0
        BNE     %BT01

11      ADR     r0, ErrorBlock_BadDevVecRel
        SETV
        B       DV_Exit

        MakeErrorBlock BadDevVecRel


02      CMP     r11, #-1
        BEQ     %FT03
        MOV     r2, r12
        LDR     r12, [r2, #8]
        STR     r12, [r11, #8]          ; node delinked
        B       %FT04

03      LDR     r2, [r12, #8]           ; freeable = nextnode
        LDMIA   r2,  {r0, r1, r3}       ; copy next node into head posn
        STMIA   r12, {r0, r1, r3}

04      MOV     r0, #HeapReason_Free
        LDR     r1, =SysHeapStart
        SWI     XOS_Heap                ; free block
        B       DV_Exit


PoduleChainRelease
        CMP     r0, #Podule_DevNo
        LDREQ   r0, =PIRQ_Chain-PodDesp_Link
        LDRNE   r0, =PFIQasIRQ_Chain-PodDesp_Link

10      LDR     r12, [r0, #PodDesp_Link]
        CMP     r12, #0
        BEQ     %BT11
        LDR     r11, [r12, #PodDesp_Address]
        CMP     r11, r3
        LDREQ   r11, [r12, #PodDesp_Mask]
        CMPEQ   r11, r4
        LDREQ   r11, [r12, #PodDesp_CallAddr]
        CMPEQ   r11, r1
        LDREQ   r11, [r12, #PodDesp_R12Val]
        CMPEQ   r11, r2
        MOVNE   r0, r12
        BNE     %BT10

        LDR     r11, [r12, #PodDesp_Link]
        STR     r11, [r0,  #PodDesp_Link]
        MOV     r2, r12
        B       %BT04

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Default device owner for IRQ not recognised by system: pass to IRQ2V

IRQ ROUT

        Push    "r10, lr"
 [ False
 MOV r14, #11
 MUL r14, r0, r14
 MOV r14, r14, LSR #5
 ADR r10, irq_vtable
 LDR r14, [r10, r14, LSL #2]
 MOV r10, #VIDC
 STR r14, [r10]
 ]
        MOV     r10, #IrqV
        BL      CallVector

        Pull    "r10, pc"               ; return: someone will always claim it.

 [ False
irq_vtable
 DCD &40000000 + &444
 DCD &40000000 + &008
 DCD &40000000 + &080
 DCD &40000000 + &088

 DCD &40000000 + &800
 DCD &40000000 + &808
 DCD &40000000 + &880
 DCD &40000000 + &FA8

 DCD &40000000 + &8AF
 DCD &40000000 + &00F
 DCD &40000000 + &0F0
 DCD &40000000 + &0FF

 DCD &40000000 + &F00
 DCD &40000000 + &F0F
 DCD &40000000 + &FF0
 DCD &40000000 + &FFF
 ]

; *****************************************************************************
; Default IRQ2V:
;   r0  must still have devno*3 in it
;   r12 is 0 (from vector)

; Clear mask, clear IRQ as appropriate/possible

; NB. a cheap way of dividing by ~3 is *11,/32: accurate for 0..31 result ...

NOIRQ ROUT

 [ False
 MOV r14, #11
 MUL r14, r0, r14
 MOV r14, r14, LSR #5
 ADR r10, irq_vtable
 LDR r14, [r10, r14, LSL #2]
 MOV r10, #VIDC
 STR r14, [r10]
 ]
01      SUBS    r0, r0, #3
        ADDGE   r12, r12, #1
        BGT     %BT01                   ; r12 := r0 DIV 3
        CMP     r12, #8
        MOVGE   r0, #IOCIRQMSKB
        SUBGE   r12, r12, #8
        MOVLT   r0, #IOCIRQMSKA
        ADD     r0, r0, #IOC
        MOV     r1, #1
        MOV     r1, r1, LSL r12         ; bit to clear

 [ :LNOT: NewClockChip                  ; fudge winnieDRQ on A500
        CMP     r1, #winnie_IRQ_bit
        CMPEQ   r0, #IOCIRQMSKB
        ORREQ   r1, r1, #winnie_DRQ_bit ; turn both off.
 ]
        MOV     lr, pc
        TEQP    pc, #IRQ_mode+I_bit+F_bit
        LDRB    r12, [r0]       ; FIQs off for updating IOCIRQMSKA
        BIC     r12, r12, r1
        STRB    r12, [r0]       ; relevant IRQ disabled
        TEQP    lr, #0          ; absolute minimum FIQ disable period

        STRB    r1, [r0, #IOCIRQCLRA-IOCIRQMSKA] ; Clear IRQ
        Pull    pc,,^           ; claim vector

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; The following bits have been appropriated from source.pmf.oseven to make
; sure Tim's old code doesn't overwrite us when he gets back!

; SWI OS_GenerateEvent: call event vector if enabled

GenEvent ROUT

        Push    lr
        TEQP    pc, #SVC_mode+I_bit     ; Disable IRQs. MUST call these ones
        BL      OSEVEN                  ; in SVC mode as people expect it
        Pull    lr
        B       SLVK

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Subroutine call version

; In    r0 = event type
;       r1,r2 parameters

; Out   C=0 => event was enabled, or was >= 32 anyway
;       C=1 => event was disabled, so vector not called

OSEVEN ROUT

        Push    lr

        CMP     r0, #31                 ; Events >= 32 are ALWAYS raised. SKS
                                        ; flags are HI if so, ie. NE
        LDRLSB  r14, [r0, #OsbyteVars + :INDEX: EventSemaphores]
                                        ; get semaphore for this event 0..31
        CMPLS   r14, #0                 ; non-zero => enabled
        Pull    pc, EQ                  ; if disabled, exit with C=1

        Push    "r0-r3, r10-r12" ; r3 excessive ???
        MOV     r10, #EventV            ; call event vector
        BL      CallVector
        CLC                             ; indicate event enabled
        Pull    "r0-r3, r10-r12, pc"

; ...................... default owner of EventV ..............................
; Call Event handler

; In    r12 = EvtHan_ws

DefEvent ROUT

        MOV     lr, pc                  ; link with all the bits
        LDMIA   r12, {r12, pc}          ; call EventHandler, returns to ...

        TEQ     r12, #1
        Pull    pc,NE,^

        LDRB    r14, [r12, #CallBack_Flag-1] ; IRQs are still disabled
        ORR     r14, r14, #CBack_OldStyle
        STRB    r14, [r12, #CallBack_Flag-1]
        Pull    pc,,^                   ; claim EventV

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Process timer zero IRQ device (100Hz clock)
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

TickOne ROUT

        Push    r14

        MOV     R0, #timer0_bit
        STRB    R0, [R3, #IOCIRQCLRA]   ; clear timer 0 interrupt

        LDRB    R0, CentiCounter        ; Counter for VDU CTRL timing
        SUBS    R0, R0, #1
        STRCSB  R0, CentiCounter        ; decrement if not zero

        LDR     R0, IntervalTimer +0
        ADDS    R0, R0, #1              ; Increment the low 4 bytes
        STR     R0, IntervalTimer +0

        LDREQB  R0, IntervalTimer +4
        ADDEQ   R0, R0, #1              ; and carry into 5th byte if necessary
        STREQB  R0, IntervalTimer +4

        Push    "R4,R12"                ; R0-R3 already pushed

        TEQEQ   R0, #&100               ; has interval timer crossed zero ?
        MOVEQ   R0, #Event_IntervalTimer ; Event ITCZ
        BLEQ    OSEVEN

        BL      CentiSecondTick         ; Notify keyboard of a centisecond

        Pull    "R4,R12"
                         
        [ :LNOT: NewClockChip
        LDRB    R1, SecondsDirty
        TEQ     R1, #0                  ; if seconds dirty
        BNE     %FT10                   ; we haven't synced yet
        ]

        LDR     R0, RealTime +0         ; Increment 5-byte real time
        ADDS    R0, R0, #1
        STR     R0, RealTime +0
        LDRCSB  R0, RealTime +4
        ADDCS   R0, R0, #1              ; Won't wrap until 2248 and then it
        STRCSB  R0, RealTime +4         ; all falls over anyway

        [ :LNOT: NewClockChip
        LDRB    R0, CentiTime
        ADD     R0, R0, #1
        TEQ     R0, #100
        MOVEQ   R0, #0
        STRB    R0, CentiTime

        LDRB    R0, SecondsTime
        ADDEQ   R0, R0, #1              ; increment only if wrap from centisecs
        TEQEQ   R0, #60
        MOVEQ   R0, #0
        STRB    R0, SecondsTime                           

        B       NoTickThisTime          ; don't do dirty code
10
        LDRB    R0, MinTick
        MOV     R1, #IOC
        LDRB    R3, [R1, #IOCControl]   ; IOC control register
        AND     R3, R3, #rtc_minutes_bit
        TEQ     R3, R0                  ; Look for transition 
        BEQ     NoTickThisTime
        TEQ     R3, #rtc_minutes_bit    ; from zero to one = minute!!!
        STRB    R3, MinTick             ; One to zero = 30 seconds
        MOV     R0, #0
        STRB    R0, SecondsDirty        ; Mark the seconds as OK now ! 
        STRB    R0, CentiTime ; When the minutes tick, ZERO the centiseconds!
        MOVEQ   R0, #30
        STRB    R0, SecondsTime         ; And set the seconds to 0 or 30 !

        ADRL    R0, OffsetMin
        LDR     R0, [R0]                ; get offset for 1 minute (60 secs)
        MOVEQ   R0, R0, LSR #1          ; halve it if 30 second tick

        LDR     R1, RealTime +0
        ADDS    R1, R1, R0
        STR     R1, RealTime +0
        LDRCSB  R1, RealTime +4
        ADDCS   R1, R1, #1
        STRCSB  R1, RealTime +4

NoTickThisTime
        ]

        LDRB    R0, TimerState          ; get switch state
        TEQ     R0, #5                  ; toggles between 5 and 10

        LDREQ   R1, TimerAlpha +0       ; either load from one
        LDREQB  R2, TimerAlpha +4

        LDRNE   R1, TimerBeta +0        ; or the other
        LDRNEB  R2, TimerBeta +4

        ADREQ   R3, TimerBeta +0        ; and point to t'other
        ADRNE   R3, TimerAlpha +0

        ADDS    R1, R1, #1              ; increment
        ADC     R2, R2, #0              ; with carry

        STR     R1, [R3]                ; and store back
        STRB    R2, [R3, #4]

        EOR     R0, R0, #&0F            ; 5 <-> 10
        STRB    R0, TimerState

        Push    R10

        BL      ProcessTickEventChain

        MOV     R10, #TickerV           ; call 100Hz vector
        BL      CallVector
        Pull    "R10,PC"

; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Process VSync IRQ device
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

VsyncIRQ ROUT

        Push    r14

        MOV     R0, #vsync_bit
        STRB    R0, [R3, #IOCIRQCLRA]   ; Clear the vsync interrupt

        LDRB    R0, CFStime             ; decrement 'CFS' timer !
        SUB     R0, R0, #1
        STRB    R0, CFStime

        MOV     R0, #Event_VSync        ; VSYNC event number
        BL      OSEVEN

        VDWS    WsPtr
        BL      VsyncCall
        BYTEWS  WsPtr

        LDRB    R1, FlashCount
        SUBS    R1, R1, #1
        Pull    PC, CC                  ; was zero, so frozen
        STRNEB  R1, FlashCount          ; else if now non-zero, store it back
        Pull    PC, NE                  ; not time to flash yet

        LDRB    R1, FlashState          ; Get the state and
        EORS    R1, R1, #1              ; flip to the other one (setting flags)
        STRB    R1, FlashState

        LDREQB  R2, SpacPeriod          ; get appropriate new period
        LDRNEB  R2, MarkPeriod
        STRB    R2, FlashCount          ; and put into counter

        VDWS    WsPtr
        Push    R4
        BEQ     dothesecondflash

dothefirstflash
        BL      DoFirstFlash
        Pull    "R4, PC"

dothesecondflash
        BL      DoSecondFlash
        Pull    "R4, PC"

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        LNK     Oscli
