; 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 ; ; ***************************************************************************** [ :LNOT: STB ALIGN 32 ] [ Interruptible32bitModes Initial_IRQ_Code ROUT SUB lr, lr, #4 Push "r0, lr" mrs AL, lr, SPSR_all 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 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 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] mrs AL, r0, CPSR_all ORR r0, r0, #&10 msr AL, CPSR_all, r0 ; switch back to IRQ32 mode LDRB r11, [r11, #CallBack_Flag] TEQ r11, #0 Pull "r1-r3, r11, r12, lr", EQ msr EQ, SPSR_all, 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 msr NE, SPSR_all, lr Pull "r0, pc", NE, ^ TST lr, #&10 ; check whether USR26 or USR32 BNE IRQ_callback32 ; Do a CallBack: asked for, not postponed, and we're returning into USR26 mode. ASSERT IRQ32_mode :AND: SVC32_mode = IRQ32_mode ; so the following dodgy ops work Pull "r1-r3, r11, r12" mrs AL, r0, CPSR_all ORR r0, r0, #SVC32_mode msr AL, CPSR_all, r0 Push "r10-r12" ; push r10-r12 onto the SVC stack BIC r0, r0, #IRQ32_mode :EOR: SVC32_mode msr AL, CPSR_all, r0 Pull "r10-r12" ; SPSR, R0, LR really BIC r0, r0, #&1F ORR r0, r0, #SVC26_mode msr AL, CPSR_all, r0 AND r0, r10, #&F0000000 ; plop flags into LR... ORR lr, r12, r0 AND r0, r10, #I32_bit:OR:F32_bit ORR lr, lr, r0, LSL #IF32_26Shift ; and the I and F flags (32-bit mode) MOV r0, r11 ; restore original R0 MOV r10, #0 LDRB r11, [r10, #CallBack_Flag] B Do_CallBack ; Do a CallBack32: asked for, not postponed, and we're returning into USR32 mode. ; IRQ_callback32 Pull "r1-r3, r11, r12" mrs AL, r0, CPSR_all ORR r0, r0, #SVC32_mode msr AL, CPSR_all, r0 Push "r10-r12" ; push r10-r12 onto the SVC stack BIC r0, r0, #IRQ32_mode :EOR: SVC32_mode msr AL, CPSR_all, r0 Pull "r10-r12" ; SPSR, R0, LR really BIC r0, r0, #&1F ORR r0, r0, #SVC26_mode msr AL, CPSR_all, r0 Push "r10" ; push SPSR onto SVC stack (PSR for return) MOV lr, r12 ; address for return MOV r0, r11 ; restore original R0 MOV r10, #0 LDRB r11, [r10, #CallBack_Flag] B Do_CallBack32 | ;Interruptible32bitModes 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" 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 ] ;Interruptible32bitModes LTORG ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Default IRQ1V: despatch on interrupting device ; Now copied to RAM, together with vector entries and device tables [ :LNOT: STB ALIGN 32 ] 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. [ STB 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 | 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 [ STB NoInterrupt * 38 ; Morris has IOMD's extra interrupts plus 16 of its own | 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 [ STB ; 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 ; Following tables encode the priority of the devices within each register ; DeviceTables [ 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 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) [ IO_Type = "IOC-A1" :LOR: IO_Type = "IOC-A500" IrqReqAPrio1 * ring_bit IrqReqADev1 * Ringing_DevNo IrqReqAPrio2 * pbusy_bit IrqReqADev2 * PrinterBusy_DevNo IrqReqAPrio3 * pack_bit IrqReqADev3 * PrinterAck_DevNo ] [ IO_Type = "IOEB" IrqReqAPrio1 * IOEB_battery_low_bit IrqReqADev1 * Ringing_DevNo IrqReqAPrio2 * pbusy_bit IrqReqADev2 * PrinterBusy_DevNo IrqReqAPrio3 * IOEB_floppy_index_bit IrqReqADev3 * PrinterAck_DevNo ] [ IO_Type = "IOMD" 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 [ 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 IrqReqBPrio7 * serial_bit IrqReqBDev7 * Serial_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 IrqReqBPrio7 * serial_bit IrqReqBDev7 * Serial_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 IrqReqBPrio7 * serial_bit IrqReqBDev7 * Serial_DevNo ] [ IO_Type = "IOMD" [ 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 [ STB ; 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 ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; 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] EOR r1, r1, r2, LSR #8 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 ; Cope with interrupts from IRQC registers ; [ STB CMP R12, #IOMD_C_Bit0_DevNo SUBHS R12, R12, #IOMD_C_Bit0_DevNo MOVHS R0, #IOMD_IRQMSKC BHS %FT03 ] ; 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) ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ [ :LNOT: STB ALIGN 32 ] 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 ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ [ :LNOT: STB ALIGN 32 ] 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