; Copyright 1996 Acorn Computers Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
        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 Pete's sake remember to change the heap manager if you change the above
; ** 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"
 ]

 [ CPU_Type = "ARM600"
        BIC     pc, pc, #&FC000000      ; get back out of shadow ROM
        NOP                             ; (this instruction skipped)
        mrs     AL, r0, SPSR_all        ; r0 = saved PSR
        AND     r1, r0, #I32_bit + F32_bit ; r1 = caller I&F flags, in ARM6 place
        ORR     lr, lr, r1, LSL #IF32_26Shift ; put IF in place
        AND     r1, r0, #&F0000003      ; r1 = caller NZCV and mode bits
        ORR     lr, lr, r1              ; lr = 26 bit style return addr + PSR
        STR     lr, [sp_irq, #4*6]      ; store back into stack

        BIC     r0, r0, #&1F            ; clear out foreground mode bits
        ORR     r0, r0, #I32_bit + IRQ26_mode ; force IRQ_26 mode and I_bit set
        msr     AL, CPSR_all, r0
 ]

        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 somebodysonIRQ1V
;  .... 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

 [ IO_Type = "IOMD"
        MOV     r3, #IOC                ; base for IOC and IOMD
        LDRB    r0, [r3, #IOMD_DMAREQ]
        TEQ     r0, #0
        ADRNE   r1, IrqDMADevnos
        LDREQB  r0, [r3, #IOCIRQREQB]   ; if not DMA then assume IRQB until we know otherwise
        ADREQ   r1, IrqReqBDevnos

 [ MorrisSupport
;>>>RCM Says should we use separate Morris specific code, cos Morris doesn't support
;>>>RCM all IOMD_DMAREQ bits and non Morris machines don't have IOMD_IRQRQD.
;>>>RCM Look at use of NoInterrupt.
        TEQEQ   r0, #0
        LDREQB  r0, [r3, #IOMD_IRQRQD]
        ADREQ   r1, IrqReqDDevnos
 ]
        TEQEQ   r0, #0
        LDREQB  r0, [r3, #IOCIRQREQA]   ; not DMA and not IRQB so assume IRQA
        ADREQ   r1, IrqReqADevnos

        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}
 |
        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

 [ IO_Type = "IOMD"
  [ MorrisSupport
NoInterrupt * 27 ; Morris has IOMD's extra interrupts plus 5 of its own
  |
NoInterrupt * 22 ; IOMD has 6 more interrupts for DMA
  ]
 |
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
 [ DriversInKernel
        & OsbyteVars
        & PrinterIRQ
        & 0
 |
        & 0
        & IRQ
        & 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
 [ DriversInKernel
        & OsbyteVars
        & RS423IRQ
        & 0
 |
        & 0
        & IRQ
        & 0
 ]
; winnie IRQ
        & 0
        & IRQ
        & 0
; Disc changed
        & 0
        & IRQ
        & 0
; podule IRQ
        & PIRQ_Chain - (PodDesp_Link-PodDesp_R12Val)
        & PIRQ_Despatch
        & 0
; serial TX     (Keyboard serial transmit register empty)
        & IOC
 [ Keyboard_Type = "A1A500"
        & IrqTx
 |
        & IRQ
 ]
        & 0
; serial RX     (Keyboard serial receive register full)
        & IOC
 [ Keyboard_Type = "A1A500"
        & IrqRx
 |
        & IRQ
 ]
        & 0

 [ IO_Type = "IOMD"
; IOMD DMA devices
; DMA channel 0
        & 0
        & IRQ
        & 0
; DMA channel 1
        & 0
        & IRQ
        & 0
; DMA channel 2
        & 0
        & IRQ
        & 0
; DMA channel 3
        & 0
        & IRQ
        & 0
; Sound DMA channel 0
        & 0
        & IRQ
        & 0
; Sound DMA channel 1
        & 0
        & IRQ
        & 0
 ]

 [ MorrisSupport
; register D devices
; Mouse port Rx full
        & IOC
        & IRQ
        & 0
; Mouse port Tx empty
        & IOC
        & IRQ
        & 0
; AtoD (Joystick)
        & 0
        & IRQ
        & 0
; Nevent1
        & 0
        & IRQ
        & 0
; Nevent2
        & 0
        & IRQ
        & 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

; generic IRQA bits
IrqReqAPrio0 * por_bit
IrqReqADev0  * PowerOn_DevNo

IrqReqAPrio2 * pbusy_bit
IrqReqADev2 * PrinterBusy_DevNo

IrqReqAPrio4 * timer1_bit
IrqReqADev4 * Timer1_DevNo

IrqReqAPrio5 * vsync_bit
IrqReqADev5 * VSync_DevNo

IrqReqAPrio6 * timer0_bit
IrqReqADev6 * Timer0_DevNo

IrqReqAPrio7 * force_bit
IrqReqADev7 * FIQDowngrade_DevNo

; Machine specific IRQB bits

 [ IO_Type = "IOC-A1" :LOR: IO_Type = "IOC-A500"
IrqReqAPrio1 * ring_bit
IrqReqADev1 * Ringing_DevNo

IrqReqAPrio3 * pack_bit
IrqReqADev3 * PrinterAck_DevNo
 ]

 [ IO_Type = "IOEB"
IrqReqAPrio1 * IOEB_battery_low_bit
IrqReqADev1 * Ringing_DevNo

IrqReqAPrio3 * IOEB_floppy_index_bit
IrqReqADev3 * PrinterAck_DevNo
 ]

 [ IO_Type = "IOMD"
IrqReqAPrio1 * 1:SHL:1          ; not used
IrqReqADev1 * Ringing_DevNo

IrqReqAPrio3 * IOMD_floppy_index_bit
IrqReqADev3 * PrinterAck_DevNo
 ]

IrqReqADevnos
  =  NoInterrupt*3
  WHILE DTabC <256
  [ (DTabC:AND:IrqReqAPrio7)<>0
  = IrqReqADev7*3
  |
  [ (DTabC:AND:IrqReqAPrio6)<>0
  = IrqReqADev6*3
  |
  [ (DTabC:AND:IrqReqAPrio5)<>0
  = IrqReqADev5*3
  |
  [ (DTabC:AND:IrqReqAPrio4)<>0
  = IrqReqADev4*3
  |
  [ (DTabC:AND:IrqReqAPrio3)<>0
  = IrqReqADev3*3
  |
  [ (DTabC:AND:IrqReqAPrio2)<>0
  = IrqReqADev2*3
  |
  [ (DTabC:AND:IrqReqAPrio1)<>0
  = IrqReqADev1*3
  |
  [ (DTabC:AND:IrqReqAPrio0)<>0
  = IrqReqADev0*3
  ]
  ]
  ]
  ]
  ]
  ]
  ]
  ]
DTabC SETA DTabC+1
  WEND

; generic IRQB bits
IrqReqBPrio2 * podule_FIQ_as_IRQ_bit
IrqReqBDev2  * PFIQasIRQ_DevNo

IrqReqBPrio3 * serial_Tx_bit
IrqReqBDev3 * SerialTx_DevNo

IrqReqBPrio4 * serial_Rx_bit
IrqReqBDev4 * SerialRx_DevNo

IrqReqBPrio5 * podule_IRQ_bit
IrqReqBDev5 * Podule_DevNo

IrqReqBPrio7 * serial_bit
IrqReqBDev7 * Serial_DevNo

; Machine specific IRQB bits

 [ IO_Type = "IOC-A500"
IrqReqBPrio0 * 0
IrqReqBDev0 * NoInterrupt

IrqReqBPrio1 * sound_IRQ_bit
IrqReqBDev1 * Sound_DevNo

IrqReqBPrio6 * winnie_IRQ_bit :OR: winnie_DRQ_bit
IrqReqBDev6 * WinnieIRQ_DevNo
 ]

 [ IO_Type = "IOC-A1"
IrqReqBPrio0 * winnie_DRQ_bit
IrqReqBDev0 * DiscChanged_DevNo

IrqReqBPrio1 * sound_IRQ_bit
IrqReqBDev1 * Sound_DevNo

IrqReqBPrio6 * winnie_IRQ_bit
IrqReqBDev6 * WinnieIRQ_DevNo
 ]

 [ IO_Type = "IOEB"
IrqReqBPrio0 * IOEB_ide_IRQ_bit
IrqReqBDev0 * DiscChanged_DevNo

IrqReqBPrio1 * IOEB_sound_IRQ_bit
IrqReqBDev1 * Sound_DevNo

IrqReqBPrio6 * IOEB_floppy_IRQ_bit
IrqReqBDev6 * WinnieIRQ_DevNo
 ]

 [ IO_Type = "IOMD"
IrqReqBPrio0 * IOMD_floppy_IRQ_bit
IrqReqBDev0 * DiscChanged_DevNo

IrqReqBPrio1 * IOMD_HardDisc_IRQ_bit
IrqReqBDev1 * Sound_DevNo

IrqReqBPrio6 * IOMD_Network_IRQ_bit
IrqReqBDev6 * WinnieIRQ_DevNo
 ]



DTabC  SETA 1

IrqReqBDevnos
  =  NoInterrupt*3

  WHILE DTabC <256
  [ (DTabC:AND:IrqReqBPrio7)<>0
  = IrqReqBDev7*3
  |
  [ (DTabC:AND:IrqReqBPrio6)<>0
  = IrqReqBDev6*3
  |
  [ (DTabC:AND:IrqReqBPrio5)<>0
  = IrqReqBDev5*3
  |
  [ (DTabC:AND:IrqReqBPrio4)<>0
  = IrqReqBDev4*3
  |
  [ (DTabC:AND:IrqReqBPrio3)<>0
  = IrqReqBDev3*3
  |
  [ (DTabC:AND:IrqReqBPrio2)<>0
  = IrqReqBDev2*3
  |
  [ (DTabC:AND:IrqReqBPrio1)<>0
  = IrqReqBDev1*3
  |
  [ (DTabC:AND:IrqReqBPrio0)<>0
  = IrqReqBDev0*3
  ]
  ]
  ]
  ]
  ]
  ]
  ]
  ]
DTabC SETA DTabC+1
  WEND


 [ IO_Type = "IOMD"
; Prioritised IOMD DMA device numbers

IrqDMAPrio0     * 1:SHL:5
IrqDMADev0      * IOMD_DMASound1_DevNo

IrqDMAPrio1     * 1:SHL:4
IrqDMADev1      * IOMD_DMASound0_DevNo

IrqDMAPrio2     * 1:SHL:3
IrqDMADev2      * IOMD_DMAChannel3_DevNo

IrqDMAPrio3     * 1:SHL:2
IrqDMADev3      * IOMD_DMAChannel2_DevNo

IrqDMAPrio4     * 1:SHL:1
IrqDMADev4      * IOMD_DMAChannel1_DevNo

IrqDMAPrio5     * 1:SHL:0
IrqDMADev5      * IOMD_DMAChannel0_DevNo


DTabC SETA 1

IrqDMADevnos
  = NoInterrupt*3

; Top 2 bits are always 0 so table need only be 64 bytes
  WHILE DTabC <64
  [ (DTabC:AND:IrqDMAPrio5)<>0
  = IrqDMADev5*3
  |
  [ (DTabC:AND:IrqDMAPrio4)<>0
  = IrqDMADev4*3
  |
  [ (DTabC:AND:IrqDMAPrio3)<>0
  = IrqDMADev3*3
  |
  [ (DTabC:AND:IrqDMAPrio2)<>0
  = IrqDMADev2*3
  |
  [ (DTabC:AND:IrqDMAPrio1)<>0
  = IrqDMADev1*3
  |
  [ (DTabC:AND:IrqDMAPrio0)<>0
  = IrqDMADev0*3
  ]
  ]
  ]
  ]
  ]
  ]
DTabC SETA DTabC+1
  WEND

 ]


 [ MorrisSupport
; Prioritised IRQD device numbers

IrqReqDPrio0     * 1:SHL:4
IrqReqDDev0      * IOMD_Event2_DevNo

IrqReqDPrio1     * 1:SHL:3
IrqReqDDev1      * IOMD_Event1_DevNo

IrqReqDPrio2     * 1:SHL:2
IrqReqDDev2      * IOMD_AtoD_DevNo

IrqReqDPrio3     * 1:SHL:1
IrqReqDDev3      * IOMD_MouseTxEmpty_DevNo

IrqReqDPrio4     * 1:SHL:0
IrqReqDDev4      * IOMD_MouseRxFull_DevNo


DTabC  SETA 1

IrqReqDDevnos
  =  NoInterrupt*3

; Top 3 bits are always 0 so table need only be 32 bytes
  WHILE DTabC <32
  [ (DTabC:AND:IrqReqDPrio4)<>0
  = IrqReqDDev4*3
  |
  [ (DTabC:AND:IrqReqDPrio3)<>0
  = IrqReqDDev3*3
  |
  [ (DTabC:AND:IrqReqDPrio2)<>0
  = IrqReqDDev2*3
  |
  [ (DTabC:AND:IrqReqDPrio1)<>0
  = IrqReqDDev1*3
  |
  [ (DTabC:AND:IrqReqDPrio0)<>0
  = IrqReqDDev0*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

 [ Fix11
; TMD 09-Jun-89: Don't corrupt r0 - it's needed by the default IRQ2 routine
        LDRB    r1, [r1]
        ANDS    r1, r1, r2
 |
        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
      [ International
        BL      TranslateError
      |
        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
      [ International
        BL      TranslateError
      |
        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
        BL      FreeSysHeapNode         ; 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
 ASSERT VIDC_Type <> "VIDC20"
 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
 ASSERT VIDC_Type <> "VIDC20"
 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

 [ MorrisSupport
; RCM 31-Jan-95 fix MED-04355, need to cope with interrupts from new Morris register
        CMP     R12, #IOMD_MouseRxFull_DevNo
        SUBHS   R12, R12, #IOMD_MouseRxFull_DevNo       ;reduce to bit number 0..7
        MOVHS   R0, #IOMD_IRQMSKD                       ; in IRQ D interrupt register
        BHS     %FT03
 ]

; ** TMD 14-Feb-94 - insert code here to fix MED-02859
; ** Old code did not cope with the DMA case here

 [ IO_Type = "IOMD"
        CMP     r12, #16                ; check if a DMA device
        BCC     %FT02                   ; it's not, so skip

        SUB     r12, r12, #16           ; convert to a bit number in mask
        MOV     r1, #1
        MOV     r1, r1, LSL r12         ; convert to bit mask
        LDR     r0, =IOC+IOMD_DMAMSK    ; point at mask register
        LDRB    r12, [r0]               ; load mask
        BIC     r12, r12, r1            ; knock out bit
        STRB    r12, [r0]               ; store back
        Pull    pc,,^                   ; and exit, claiming vector

02
 ]

; ** end of insertion

        CMP     r12, #8
        MOVGE   r0, #IOCIRQMSKB
        SUBGE   r12, r12, #8
        MOVLT   r0, #IOCIRQMSKA
 [ MorrisSupport
03
 ]
        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 !

        LDR     R0, =ticksperminute     ; 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

 [ TickIrqReenter
        MOV     R10, #TickerV           ; call 100Hz vector
        BL      CallVector              ; IRQ's still disabled

        BL      ProcessTickEventChain   ; Re-enables IRQs
 |
        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

        VDWS    WsPtr                   ; Do our stuff before issuing VSYNC event
        BL      VsyncCall
        BYTEWS  WsPtr

        MOV     R0, #Event_VSync        ; VSYNC event number
        BL      OSEVEN

        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"

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

        END