; 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 ; ; ***************************************************************************** ALIGN 32 Initial_IRQ_Code ROUT SUB lr, lr, #4 Push "r0, lr" MRS lr, SPSR Push "r1-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*5] below [ :LNOT:No26bitCode BIC r0, lr, #&1F ; clear out foreground mode bits ORR r0, r0, #I32_bit + IRQ26_mode ; force IRQ_26 mode and I bit set MSR CPSR_c, r0 ] LDR r12, =ZeroPage 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 Push "r10" MOV r10, #UnthreadV BL CallVector Pull "r10" LDR r11, =ZeroPage Pull r0 STR r0, [r11, #IRQsema] [ :LNOT:No26bitCode MRS r0, CPSR ORR r0, r0, #&10 MSR CPSR_c, r0 ; switch back to IRQ32 mode ] LDRB r11, [r11, #CallBack_Flag] TEQ r11, #0 Pull "r1-r3, r11, r12, lr", EQ MSREQ SPSR_cxsf, lr Pull "r0, pc", EQ, ^ TST r11, #CBack_Postpone LDREQ lr, [sp_irq, #4*5] ; get SPSR off stack TSTEQ lr, #I32_bit :OR: &0F ; check we came from USR26 or USR32 mode, with IRQs enabled Pull "r1-r3, r11, r12, lr", NE MSRNE SPSR_cxsf, lr Pull "r0, pc", NE, ^ ; Do a CallBack: asked for, not postponed, and we're returning into USR26/32 mode. ASSERT IRQ32_mode :AND: SVC32_mode = IRQ32_mode ; so the following dodgy ops work Pull "r1-r3, r11, r12" MRS r0, CPSR ORR r0, r0, #SVC32_mode MSR CPSR_c, r0 Push "r10-r12" ; push r10-r12 onto the SVC stack BIC r0, r0, #IRQ32_mode :EOR: SVC32_mode MSR CPSR_c, r0 Pull "r10-r12" ; SPSR, R0, LR really [ No26bitCode :LOR: FixCallBacks ORR r0, r0, #SVC32_mode | BIC r0, r0, #&1F ORR r0, r0, #SVC26_mode ] MSR CPSR_c, r0 Push r12 ; Save the return address MOV r14, r10 ; SPSR into R14 MOV r0, r11 ; restore original R0 LDR r10, =ZeroPage LDRB r11, [r10, #CallBack_Flag] [ FixCallBacks B Do_CallBack_postpone_already_clear | B Do_CallBack ] LTORG ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Default IRQ1V: despatch on interrupting device ; Now copied to RAM, together with vector entries and device tables ^ 0 [ HAL IRQDesp_Link # 4 IRQDesp_R12Val # 4 IRQDesp_CallAddr # 4 IRQDesp_Link_Unshared * 1 ; flag in Link (for _this_ node) | IRQDesp_R12Val # 4 IRQDesp_CallAddr # 4 IRQDesp_Link # 4 ] ASSERT IRQDesp_CallAddr = IRQDesp_R12Val + 4 ALIGN 32 DefaultIRQ1Vcode ROUT [ HAL Push "r9,lr" [ ZeroPage = 0 MOV r9, #0 | LDR r9, %FT02 ] AddressHAL r9 ; modifies r9 ; MOV r11, r14 ; r11 trashable CallHAL HAL_IRQSource Pull "r9" ADR r2, Devices ADD r1, r0, r0, LSL #1 ; multiply by 3 ; MOV r14, r11 ADD r11, r2, r1, LSL #2 ; so table contains DevNo * 3 ; ASSERT IRQDesp_R12Val = 4 ; LDMIB r1, {r12, pc} 01 MOV lr, pc LDMIA r11, {r11, r12, pc} TST r11, #IRQDesp_Link_Unshared BEQ %BT01 Pull pc [ ZeroPage <> 0 02 DCD ZeroPage ] | 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. ADREQ r12, DeviceTables ; can't reach these tables with ADR TEQEQ r0, #0 LDREQB r0, [r3, #IOMD_IRQRQD] ADDEQ r1, r12, #IrqReqDDevnos-DeviceTables TEQEQ r0, #0 LDREQB r0, [r3, #IOMD_IRQRQC] ADDEQ r1, r12, #IrqReqCDevnos-DeviceTables ] ; MorrisSupport 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 ASSERT IRQDesp_R12Val = 0 LDMIA r1, {r12, pc} ] ; HAL ; ******* IRQ device handlers entered with r0-r3,r11,r12,r14 trashable ******* ; r0 = device number (if HAL) ; r3 -> IOC (not in HAL world, unless IOMD HAL is helpful) ; r12 = what they asked for ; r14 = return address to MOS IRQ exit sequence [ HAL DefaultIRQ1Vcode_end Devices * DefaultIRQ1Vcode_end + 12 NoInterrupt * -1 [ M_CortexA9 MaxInterrupts * 160 | MaxInterrupts * 96 ] ; M_CortexA9 DevicesEnd * Devices + MaxInterrupts * 12 ASSERT DevicesEnd - DefaultIRQ1Vcode <= DefIRQ1Vspace | ; HAL [ MorrisSupport NoInterrupt * 38 ; Morris has IOMD's extra interrupts plus 16 of its own | NoInterrupt * 22 ; IOMD has 6 more interrupts for DMA ] Devices ; Register A devices ; pbusy handler & 0 ; R12 value & IRQ ; call address & 0 ; link ; ringing handler & 0 & IRQ & 0 ; printer acknowledge & 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 & 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 ; 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 ; The following are just place fillers in case IRQD bits 5 to 7 are ever used. ; NoInterrupt & 0 & IRQ & 0 ; NoInterrupt & 0 & IRQ & 0 ; NoInterrupt & 0 & IRQ & 0 ; register C devices ; Bit0 & 0 & IRQ & 0 ; Bit1 & 0 & IRQ & 0 ; Bit2 & 0 & IRQ & 0 ; Bit3 & 0 & IRQ & 0 ; Bit4 & 0 & IRQ & 0 ; Bit5 & 0 & IRQ & 0 ; Bit6 & 0 & IRQ & 0 ; Bit7 & 0 & IRQ & 0 ] ; Neither A or B is interrupting, which is impossible: just call IRQ2V anyway & 0 & IRQ & 0 DevicesEnd ; Following tables encode the priority of the devices within each register ; DeviceTables ; 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 GBLA DTabC 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 ; generic IRQA bits IrqReqAPrio0 * por_bit IrqReqADev0 * PowerOn_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 (devices 0-2) IrqReqAPrio1 * 1:SHL:1 ; not used IrqReqADev1 * 1 [ ReassignedIOMDInterrupts ASSERT IOMDr_PrinterIRQ_DevNo = 2 IrqReqAPrio2 * IOMDr_printer_IRQ_bit IrqReqADev2 * IOMDr_PrinterIRQ_DevNo IrqReqAPrio3 * 1:SHL:0 ; not used IrqReqADev3 * 0 | ASSERT IOMD_PrinterIRQ_DevNo = 0 ASSERT IOMD_FloppyIndex_DevNo = 2 IrqReqAPrio2 * IOMD_printer_IRQ_bit IrqReqADev2 * IOMD_PrinterIRQ_DevNo IrqReqAPrio3 * IOMD_floppy_index_bit IrqReqADev3 * IOMD_FloppyIndex_DevNo ] DTabC SETA 1 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 ; Machine specific IRQB bits [ ReassignedIOMDInterrupts IrqReqBPrio0 * IOMDr_MPEGAudio_IRQ_bit IrqReqBDev0 * IOMDr_MPEGAudio_DevNo IrqReqBPrio1 * IOMDr_MPEGVideo_IRQ_bit IrqReqBDev1 * IOMDr_MPEGVideo_DevNo IrqReqBPrio6 * IOMDr_Network_IRQ_bit IrqReqBDev6 * IOMDr_Network_DevNo IrqReqBPrio7 * IOMDr_serial_IRQ_bit IrqReqBDev7 * IOMDr_Serial_DevNo | IrqReqBPrio0 * IOMD_floppy_IRQ_bit IrqReqBDev0 * DiscChanged_DevNo IrqReqBPrio1 * IOMD_HardDisc_IRQ_bit IrqReqBDev1 * Sound_DevNo IrqReqBPrio6 * IOMD_Network_IRQ_bit IrqReqBDev6 * WinnieIRQ_DevNo IrqReqBPrio7 * IOMD_serial_IRQ_bit IrqReqBDev7 * IOMD_Serial_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 [ 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 (this will ; need to change if bits 5 to 7 are ever used). 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 ; Prioritised IRQC device numbers. We have to handle ALL interrupts ; using specific device numbers as we don't know what the IO pins ; are connected to and the NOIRQ code has to know what bit to clear ; when an unknown interrupt is triggered. IrqReqCPrio0 * 1:SHL:0 IrqReqCDev0 * IOMD_C_Bit0_DevNo IrqReqCPrio1 * 1:SHL:1 IrqReqCDev1 * IOMD_C_Bit1_DevNo IrqReqCPrio2 * 1:SHL:2 IrqReqCDev2 * IOMD_C_Bit2_DevNo IrqReqCPrio3 * 1:SHL:3 IrqReqCDev3 * IOMD_C_Bit3_DevNo IrqReqCPrio4 * 1:SHL:4 IrqReqCDev4 * IOMD_C_Bit4_DevNo IrqReqCPrio5 * 1:SHL:5 IrqReqCDev5 * IOMD_C_Bit5_DevNo IrqReqCPrio6 * 1:SHL:6 IrqReqCDev6 * IOMD_C_Bit6_DevNo IrqReqCPrio7 * 1:SHL:7 IrqReqCDev7 * IOMD_C_Bit7_DevNo DTabC SETA 1 IrqReqCDevnos = NoInterrupt*3 WHILE DTabC <256 [ (DTabC:AND:IrqReqCPrio7)<>0 = IrqReqCDev7*3 | [ (DTabC:AND:IrqReqCPrio6)<>0 = IrqReqCDev6*3 | [ (DTabC:AND:IrqReqCPrio5)<>0 = IrqReqCDev5*3 | [ (DTabC:AND:IrqReqCPrio4)<>0 = IrqReqCDev4*3 | [ (DTabC:AND:IrqReqCPrio3)<>0 = IrqReqCDev3*3 | [ (DTabC:AND:IrqReqCPrio2)<>0 = IrqReqCDev2*3 | [ (DTabC:AND:IrqReqCPrio1)<>0 = IrqReqCDev1*3 | [ (DTabC:AND:IrqReqCPrio0)<>0 = IrqReqCDev0*3 ] ] ] ] ] ] ] ] DTabC SETA DTabC+1 WEND ] DefaultIRQ1Vcode_end ASSERT DefaultIRQ1Vcode_end - DefaultIRQ1Vcode <= DefIRQ1Vspace ] ; :LNOT: HAL ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ InitialiseIRQ1Vtable Push "v1-v3,sb,lr" ; copy IRQ handler: not done with rest of copying ; because soft break needs the info to free any claimed blocks. LDR a1, =DefaultIRQ1V ADRL a2, DefaultIRQ1Vcode ADRL a3, DefaultIRQ1Vcode_end CopyDefaultIRQ1V LDR a4, [a2], #4 STR a4, [a1], #4 CMP a2, a3 BNE CopyDefaultIRQ1V [ HAL AddressHAL ADD v1, a1, #12 ADD a3, v1, #MaxInterrupts*12 MOV a2, #0 MOV a4, #-1 LDR ip, =IRQ FillInDefaultIRQ1VDevices STMIA a1!, {a2, a4, ip} ADD a4, a4, #1 CMP a1, a3 BNE FillInDefaultIRQ1VDevices ; Now fill in our basic device handlers. First the timer. MOV a1, #0 CallHAL HAL_TimerDevice LDR a2, =ZeroPage+OsbyteVars LDR a3, =TickOne ADD a1, a1, a1, LSL #1 ADD a1, v1, a1, LSL #2 STMIB a1, {a2, a3} ; Now the VSync MOV a1, #0 CallHAL HAL_VideoFlybackDevice CMP a1, #-1 LDRNE a2, =ZeroPage+OsbyteVars LDRNE a3, =VsyncIRQ ADDNE a1, a1, a1, LSL #1 ADDNE a1, v1, a1, LSL #2 STMNEIB a1, {a2, a3} ; BNE %ft1 ; ; here if no vsync handler .. fudge it from tickerv ; MOV a1, #TickerV ; LDR a2, =FalseVsyncIRQ ; LDR a3, =OsbyteVars ; SWI XOS_Claim 1 [ CDVPoduleIRQs ; Now Podule bits MOV a1, #IRQDesp_Link_Unshared LDR a2, =ZeroPage+PFIQasIRQ_Chain - (PodDesp_Link-PodDesp_R12Val) ADR a3, PFIQasIRQ_Despatch ADD lr, v1, #8*12 STMIA lr, {a1, a2, a3} LDR a2, =ZeroPage+PIRQ_Chain - (PodDesp_Link-PodDesp_R12Val) ADR a3, PIRQ_Despatch ADD lr, v1, #13*12 STMIA lr, {a1, a2, a3} ] ; Now IIC - if any LDR v2, =ZeroPage+IICBus_Base MOV v3, #0 80 LDR a1, [v2, #IICBus_Type] TST a1, #IICFlag_HighLevel TSTNE a1, #IICFlag_Background BEQ %FT90 SUB sp, sp, #12 MOV a1, sp MOV a2, v3 CallHAL HAL_IICDevice ; I think it's safe to call A SWI here... LDMIA sp!, {r0, r3, r4} LDR r1, =IICIRQ MOV r2, v3 SWI XOS_ClaimDeviceVector 90 ADD v2, v2, #IICBus_Size ADD v3, v3, #1 CMP v3, #IICBus_Count BNE %BT80 ] Pull "v1-v3,sb,pc" ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; 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 EOR (Mask>>8)) AND Mask <> 0 PodDesp_Link # 4 ; next node PodDesp_NodeSize # 0 [ HAL:LAND:{FALSE} ROUT ; In r1 -> top level node ; r12 = sub chain 01 LDR r12, [r12, #PodDesp_Link-PodDesp_R12Val] SubInterrupt_Despatch LDMIA r12!, {r2, r3} ; address and mask CMP r3, #&10000 LDRLOB r2, [r2] BHS %FT02 EOR r2, r2, r3, LSR #8 ; polarity inversion TST r2, r3 ; check against mask BEQ %BT01 LDMIA r12, {r12, pc} 02 Push "r0,r1,r12,lr" MOV r0, r3 MOV r12, r3 MOV lr, pc MOV pc, r2 TEQ r0, #0 Pull "r0,r1,r12,lr" BEQ %BT01 LDMIA r12, {r12, pc} NotSubInterrupt LDR r1, [r1, #8] ; call next (full) handler LDMIA r1, {r12, pc} | ; 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 01 LDR r12, [r12, #PodDesp_Link-PodDesp_R12Val] LDMIA r12!, {r1, r2} ; address and mask ; TMD 09-Jun-89: Don't corrupt r0 - it's needed by the default IRQ2 routine LDRB r1, [r1] EOR r1, r1, r2, LSR #8 ANDS r1, r1, r2 BEQ %BT01 LDMIA r12, {r12, pc} ] Default_SubInterruptHandler_Node Default_PIRQHandler_Node Default_PFIQasIRQHandler_Node & .+4 ; address we know has non-zero value! & -1 ; mask & 0 ; handler r12 [ HAL:LAND:{FALSE} & NotSubInterrupt ; handler code | & IRQ ; handler code ] & 0 ; null link for naff release checking ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Claim of device vectors ; r0 = Device number ; r1 = call address ; r2 = r12 value [ HAL ; r3 = interrupt location or r3 = 0 or (if r4 > 64K) r3 = routine ; r4 = interrupt mask/polarity r4 = workspace | [ CDVPoduleIRQs ; r0 = PFIQ|PIRQ devno -> r3 = interrupt location ; r4 = interrupt mask ] ] CDV_Flags * &FF000000 CDV_Shared * 1:SHL:31 DeviceVector_Claim ROUT Push "r0-r3, lr" 01 SWI XOS_ReleaseDeviceVector ; Release until bored BVC %BT01 LDR r0, [sp] [ HAL BIC r0, r0, #CDV_Flags ] CMP r0, #MaxInterrupts BHS DV_Fail_NaffDevNo [ HAL:LAND:{FALSE} ; TEQ r3, #0 ; BNE SubInterruptClaim | [ CDVPoduleIRQs CMP r0, #Podule_DevNo CMPNE r0, #PFIQasIRQ_DevNo BEQ PoduleChainClaim ] ] MOV r3, #12 BL ClaimSysHeapNode BVS DV_Exit LDR r11, [sp] BIC r0, r11, #CDV_Flags ADD r0, r0, r0, LSL #1 ; *3 LDR r1, =DefaultIRQ1V-DefaultIRQ1Vcode+Devices ADD r1, r1, r0, LSL #2 WritePSRc SVC_mode+I_bit, r10 ; IRQs off for update (on again on SWI exit) LDMIA r1, {r0, r3, r10} STMIA r2, {r0, r3, r10} ; copy current head into node [ HAL MOV r10, r2 TST r11, #CDV_Shared ORREQ r10, r10, #IRQDesp_Link_Unshared LDR r11, [sp, #4*2] ; r12 value LDR r12, [sp, #4*1] ; call address | 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 [ HAL:LAND:{FALSE} SubInterruptClaim Push "r5" ADD r0, r0, r0, LSL #1 ; *3 LDR r5, =DefaultIRQ1V-DefaultIRQ1Vcode+Devices ADD r5, r5, r0, LSL #2 LDR r2, [r5, #IRQDesp_CallAddr] ADR r3, SubInterrupt_Despatch TEQ r2, r3 BEQ AlreadySubbed ; Need to claim the top-level interrupt. MOV r3, #12 BL ClaimSysHeapNode Pull "r5",VS BVS DV_Exit WritePSRc SVC_mode+I_bit, r10 ; IRQs off for update (on again on SWI exit) LDMIA r5, {r0, r3, r10} STMIA r2, {r0, r3, r10} ; copy current head into node ADR r0, Default_SubInterruptHandler_Node ADR r3, SubInterrupt_Despatch STR r0, [r5, #IRQDesp_R12Val] STR r3, [r5, #IRQDesp_CallAddr] STR r2, [r5, #IRQDesp_Link] AlreadySubbed ; r1 -> top level interrupt entry MOV r3, #PodDesp_NodeSize BL ClaimSysHeapNode Pull "r5",VS BVS DV_Exit MOV r10, r2 ADD r1, sp, #8 LDMFD r1, {r1-r3} STR r1, [r10, #PodDesp_CallAddr] STR r2, [r10, #PodDesp_R12Val] STR r3, [r10, #PodDesp_Address] STR r4, [r10, #PodDesp_Mask] WritePSRc SVC_mode+I_bit, r2 ; IRQs off for update LDR r0, [r5, #IRQDesp_R12Val] STR r0, [r10, #PodDesp_Link] STR r10, [r5, #IRQDesp_R12Val] Pull "r5" B DV_Exit ] [ CDVPoduleIRQs 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, =ZeroPage+PIRQ_Chain LDRNE r0, =ZeroPage+PFIQasIRQ_Chain WritePSRc SVC_mode+I_bit, r1 ; 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 [ HAL BIC r0, r0, #CDV_Flags ] CMP r0, #MaxInterrupts BHS DV_Fail_NaffDevNo WritePSRc SVC_mode + I_bit, r12 ; IRQs off while holding context [ CDVPoduleIRQs 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 [ HAL ; TEQ r3, #0 ; BNE SubInterruptRelease ] [ HAL 01 LDMIB r12, {r3, r10} | 01 LDMIA r12, {r3, r10} ] CMP r3, r2 CMPEQ r10, r1 BEQ %FT02 ; found it MOV r11, r12 LDR r12, [r12, #IRQDesp_Link] [ HAL BICS r12, r12, #IRQDesp_Link_Unshared | 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, #IRQDesp_Link] [ HAL LDR r14, [r11, #IRQDesp_Link] ; preserve r11's "unshared" flag BIC r12, r12, #IRQDesp_Link_Unshared AND r14, r14, #IRQDesp_Link_Unshared ORR r12, r12, r14 ] STR r12, [r11, #IRQDesp_Link] ; node delinked B %FT04 03 LDR r2, [r12, #IRQDesp_Link]; freeable = nextnode [ HAL BIC r2, r2, #IRQDesp_Link_Unshared ] LDMIA r2, {r0, r1, r3} ; copy next node into head posn STMIA r12, {r0, r1, r3} 04 BL FreeSysHeapNode ; free block B DV_Exit [ HAL:LAND:{FALSE} SubInterruptRelease ADR r10, SubInterrupt_Despatch 10 LDR r0, [r12, #IRQDesp_CallAddr] TEQ r0, r10 BEQ %FT15 13 MOV r11, r12 LDR r12, [r12, #IRQDesp_Link] TEQ r12, #0 BEQ %BT11 B %BT10 15 SUB r0, r12, #PodDesp_Link 17 LDR r14, [r0, #PodDesp_Link] TEQ r14, #0 BEQ %BT13 LDR r10, [r14, #PodDesp_Address] CMP r10, r3 LDREQ r10, [r14, #PodDesp_Mask] CMPEQ r10, r4 LDREQ r10, [r14, #PodDesp_CallAddr] CMPEQ r10, r1 LDREQ r10, [r14, #PodDesp_R12Val] CMPEQ r10, r2 MOVNE r0, r14 BNE %BT17 LDR r10, [r14, #PodDesp_Link] STR r10, [r0, #PodDesp_Link]! LDR r0, [r12, #IRQDesp_R12Val] MOV r2, r14 ADR r14, Default_SubInterruptHandler_Node TEQ r0, r14 ; last sub-interrupt gone? BNE %BT04 BL FreeSysHeapNode ; free sub-interrupt BVS DV_Exit B %BT02 ; then go back to delink top level ] [ CDVPoduleIRQs PoduleChainRelease CMP r0, #Podule_DevNo LDREQ r0, =ZeroPage+PIRQ_Chain-PodDesp_Link LDRNE r0, =ZeroPage+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 [ HAL Pull "r10, lr, pc" ; new-style CDV - pull return address | 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: [ HAL ; r0 must still have devno in it ; r12 is 0 (from vector) | ; 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] ] [ HAL TEQ r0, #0 Pull pc, MI MOV r11, r9 AddressHAL CallHAL HAL_IRQDisable MOV r9, r11 | 01 SUBS r0, r0, #3 ADDGE r12, r12, #1 BGT %BT01 ; r12 := r0 DIV 3 CMP R12, #8 MOVLO R0, #IOCIRQMSKA BLO %FT03 CMP R12, #16 SUBLO R12, R12, #8 MOVLO R0, #IOCIRQMSKB BLO %FT03 CMP R12, #IOMD_MouseRxFull_DevNo SUBLO R12, R12, #IOMD_DMAChannel0_DevNo MOVLO R0, #IOMD_DMAMSK BLO %FT03 [ MorrisSupport CMP R12, #IOMD_C_Bit0_DevNo SUBLO R12, R12, #IOMD_MouseRxFull_DevNo ;reduce to bit number 0..7 MOVLO R0, #IOMD_IRQMSKD ; in IRQ D interrupt register SUBHS R12, R12, #IOMD_C_Bit0_DevNo MOVHS R0, #IOMD_IRQMSKC ] 03 ADD r0, r0, #IOC MOV r1, #1 MOV r1, r1, LSL r12 ; bit to clear MRS lr, CPSR BIC r12, lr, #&0F ORR r12, r12, #I32_bit+F32_bit+IRQ_mode MSR CPSR_c, r12 LDRB r12, [r0] ; FIQs off for updating IOCIRQMSKA BIC r12, r12, r1 STRB r12, [r0] ; relevant IRQ disabled MSR CPSR_c, lr ; 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 WritePSRc SVC_mode+I_bit, lr ; 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 [ ZeroPage = 0 LDRLSB r14, [r0, #OsbyteVars + :INDEX: EventSemaphores] | LDRLS r14, =ZeroPage+OsbyteVars+:INDEX:EventSemaphores LDRLSB r14, [r0, r14] ] ; 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 [ ZeroPage = 0 LDRB r14, [r12, #CallBack_Flag-1] ; IRQs are still disabled ORR r14, r14, #CBack_OldStyle STRB r14, [r12, #CallBack_Flag-1] | LDR r12, =ZeroPage LDRB r14, [r12, #CallBack_Flag] ; IRQs are still disabled ORR r14, r14, #CBack_OldStyle STRB r14, [r12, #CallBack_Flag] ] Pull pc ; claim EventV ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Process timer zero IRQ device (100Hz clock) ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ALIGN 32 TickOne ROUT [ HAL ; Don't push r14 - we're using new interface, and claim the vector Push "r9,r12" AddressHAL CallHAL HAL_IRQClear Pull "r9,r12" | Push r14 MOV R0, #timer0_bit STRB R0, [R3, #IOCIRQCLRA] ; clear timer 0 interrupt ] ! 0, "FIXME: temporary code" ;!!! HAPPY HACK STMFD sp!,{r0-r12} ADR r0,ret_from_vs Push r0 B VsyncIRQ_ExtEntry ret_from_vs LDMFD sp!,{r0-r12} ;!!! HAPPY HACK LDR R1, =ZeroPage LDR R0, [R1, #MetroGnome] ADD R0, R0, #1 STR R0, [R1, #MetroGnome] 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" 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 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 ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ALIGN 32 FalseVsyncIRQ ROUT LDR R1, =ZeroPage LDR R0, [R1, #MetroGnome] TST R0, #1 MOVEQ pc, lr Push "lr" B VsyncIRQ_ExtEntry VsyncIRQ ROUT [ HAL ; Don't push r14 - we're using new interface, and claim the vector Push "r9,r12" AddressHAL CallHAL HAL_IRQClear Pull "r9,r12" | Push r14 MOV R0, #vsync_bit STRB R0, [R3, #IOCIRQCLRA] ; Clear the vsync interrupt ] VsyncIRQ_ExtEntry 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