; 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 [ 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" ] 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 [ IOMD1Support ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Default IRQ1V for IOMD1 platforms: despatch on interrupting device ; Now copied to RAM, together with vector entries and device tables IOMD1_DefaultIRQ1Vcode ROUT MOV r3, #IOC ; base for IOC and IOMD LDRB r0, [r3, #IOMD_DMAREQ] TEQ r0, #0 ADRNE r1, IOMD1_IrqDMADevnos LDREQB r0, [r3, #IOCIRQREQB] ; if not DMA then assume IRQB until we know otherwise ADREQ r1, IOMD1_IrqReqBDevnos ;>>>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, IOMD1_IrqReqDDevnos TEQEQ r0, #0 LDREQB r0, [r3, #IOCIRQREQA] ; not DMA and not IRQB so assume IRQA ADREQ r1, IOMD1_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} ; ******* 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 IOMD1_NoInterrupt * 27 ; Morris has IOMD's extra interrupts plus 5 of its own IOMD1_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 & IRQ & 0 ; serial RX (Keyboard serial receive register full) & IOC & 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 ; 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 ; IOMD1_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 IOMD1_IrqDMADevnos = IOMD1_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 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 IrqReqAPrio1 * 1:SHL:1 ; not used IrqReqADev1 * Ringing_DevNo IrqReqAPrio3 * IOMD_floppy_index_bit IrqReqADev3 * PrinterAck_DevNo DTabC SETA 1 IOMD1_IrqReqADevnos = IOMD1_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 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 IOMD1_IrqReqBDevnos = IOMD1_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 ; 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 IOMD1_IrqReqDDevnos = IOMD1_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 IOMD1_DefaultIRQ1Vcode_end ASSERT IOMD1_DefaultIRQ1Vcode_end - IOMD1_DefaultIRQ1Vcode <= DefIRQ1Vspace ] ;IOMD1Support [ IOMD2Support ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Default IRQ1V for IOMD2 platforms: despatch on interrupting device ; copied to RAM, together with vector entries and device tables ; Combo interrupts are all ORR'd as SMI and wired to external interrupt 10 (ei10) on IOMD2 ; Hence, interrupts must be discriminated via Combo SMI status registers ; SMI_ComboBase * &03010000 ;combo mapped here SMI_selectreg * &3a8 ;write here to select register for access SMI_accessreg * &3ac ;access register here SMI_enable1 * &c ;value to select enable1 SMI_enable2 * &d ;value to select enable2 SMI_status1 * &e ;value to select status1 SMI_status2 * &f ;value to select status2 SMI_INTREQbit * 1 :SHL: 10 ;SMI at ei10, which is bit 10 of INTREQ IOMD2_DefaultIRQ1Vcode ROUT MOV r3, #IOC ; ; check for interrupting DMA device (similar to processing of INTREQ below, see comments there for description) ; LDR r0, [r3, #IOMD2_DMAREQ] TEQ r0, #0 BEQ %FT10 ASSERT IOMD2_DMACheck = IOMD2_DMA_HalfMask1+4 ORR r0, r0, #&80000000 ADR r1, IOMD2_DMA_HalfMask1 LDR r12, [r1], #4 TST r0, r12 ADDEQ r1, r1, #IOMD2_DMA_HalfSize 05 LDRB r12, [r1], #1 MOVS r12, r0, LSL r12 BCC %BT05 ADD r1, r1, #IOMD2_DMADevice-IOMD2_DMACheck-1 B %FT40 10 ; ; check for interrupting device either directly through IOMD2_INTREQ or through Combo SMI ; LDR r0, [r3, #IOMD2_INTREQ] BIC r0, r0, #&7E000000 ;clear pseudo bits (25..30), that may receive Combo bits ORR r0, r0, #&80000000 ;set fail safe pseudo bit (31), to guarantee terminated device loop TST r0, #SMI_INTREQbit ;check for ei10 (one or more interrupts from Combo) BEQ %FT20 ; ; pick up Combo interrupts and munge them into pseudo bits in r0 ; this is tedious, because of indexed register access, split of interrupts over two registers, and lack of ; register(s) giving pending-and-enabled status ; ASSERT SMI_ComboBase < IOC SUB r3, r3, #IOC-SMI_ComboBase ;trick to get SMI_ComboBase in r3 MOV r12, #SMI_status1 STR r12, [r3, #SMI_selectreg] LDRB r12, [r3, #SMI_accessreg] ;pending IRQs, bits 2..4 are serial2,serial1,floppy MOV r1, #SMI_enable1 STR r1, [r3, #SMI_selectreg] LDRB r1, [r3, #SMI_accessreg] ;enabled IRQs AND r12, r12, r1 ;pending and enabled IRQs ORR r0, r0, r12, LSL #23 ;munge SMI1 bits 2..4 into pseudo bits 25..27 MOV r12, #SMI_status2 STR r12, [r3, #SMI_selectreg] LDRB r12, [r3, #SMI_accessreg] ;pending IRQs, bits 0..2 are mouse,keyboard,IR MOV r1, #SMI_enable2 STR r1, [r3, #SMI_selectreg] LDRB r1, [r3, #SMI_accessreg] ;enabled IRQs AND r12, r12, r1 ;pending and enabled IRQs ORR r0, r0, r12, LSL #28 ;munge SMI2 bits 0..2 into pseudo bits 28..30 MOV r3, #IOC 20 ; ; - find highest priority interrupting device ; - serial loop may look a bit slow, but priority encoded table(s) much more unwieldy for IOMD2, ; and not necessarily faster on a cached processor (where the small tables used here will ; hopefully cache efficiently) ; - we do have a two-stage binary chop to speed things up a little ; - what we should have done is designed some configurable h/w priority encoding in IOMD2 ; ASSERT IOMD2_IRQCheck = IOMD2_IRQ_HalfMask1+3*4 ADR r1, IOMD2_IRQ_HalfMask1 LDR r12, [r1],#3*4 ;r1 now -> IOMD2_IRQCheck TST r0, r12 ;see if any bits in upper half of priority list set LDREQ r12, [r1, #-4] ;if not, pick up bits in 3rd quarter of priority list ... ADDEQ r1, r1, #IOMD2_IRQ_HalfSize ;... and skip to lower half of list LDRNE r12, [r1, #-2*4] ;if so, pick up bits in 1st quarter of priority list TST r0, r12 ;see if any bits in upper quarter of this half set ADDEQ r1, r1, #IOMD2_IRQ_QuartSize ;if not, skip to lower quarter 30 LDRB r12, [r1],#1 ;next device MOVS r12, r0, LSL r12 BCC %BT30 ;carry clear if this device not interrupting ; ; - found an interrupting device in IOMD2_IRQCheck at r1-1 ; - point r1 at corresponding entry in IOMD2_IRQDevice ; - possible improvement here is to order the IOMD2_Devices list in priority order, to ; eliminate need for IOMD2_IRQDevice look-up - claim and release of devices ; would then have to map external device numbers to internal (priority order) numbers ; ADD r1, r1, #IOMD2_IRQDevice-IOMD2_IRQCheck-1 40 LDRB r0, [r1] ;get device number*3 ADD r1, pc, r0, LSL #2 ; -> entry in IOMD2_Devices 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 IOMD2_NoInterrupt * 42 IOMD2_Devices ; 0 parallel & 0 ; R12 value & IRQ ; call address & 0 ; link ; 1 unused & 0 & IRQ & 0 ; 2 floppy index & 0 & IRQ & 0 ; 3 vsync & OsbyteVars & VsyncIRQ & 0 ; 4 unused (power on reset absent on IOMD2) & 0 & IRQ & 0 ; 5 timer0 & OsbyteVars & TickOne & 0 ; 6 timer1 & 0 & IRQ & 0 ; 7 FIQ downgrade & 0 & IRQ & 0 ; 8 PFIQ downgrade & PFIQasIRQ_Chain - (PodDesp_Link-PodDesp_R12Val) & PFIQasIRQ_Despatch & 0 ; 9 IDE A & 0 & IRQ & 0 ; 10 serial 1 & 0 & IRQ & 0 ; 11 unused (NIC nextwork card not present on Phoebe) & 0 & IRQ & 0 ; 12 floppy & 0 & IRQ & 0 ; 13 podule IRQ & PIRQ_Chain - (PodDesp_Link-PodDesp_R12Val) & PIRQ_Despatch & 0 ; 14 PS/2 keyboard & IOC & IRQ & 0 ; 15 PS/2 mouse & IOC & IRQ & 0 ; 16 DMA channel 0 & 0 & IRQ & 0 ; 17 DMA channel 1 & 0 & IRQ & 0 ; 18 DMA channel 2 & 0 & IRQ & 0 ; 19 DMA channel 3 & 0 & IRQ & 0 ; 20 Sound DMA channel 0 & 0 & IRQ & 0 ; 21 Sound DMA channel 1 (absent on IOMD2) & 0 & IRQ & 0 ; absent devices 22 ... 26 (7500 only) ; 22 & 0 & IRQ & 0 ; 23 & 0 & IRQ & 0 ; 24 & 0 & IRQ & 0 ; 25 & 0 & IRQ & 0 ; 26 & 0 & IRQ & 0 ;new IOMD2 devices ; 27 DMA channel 4 & 0 & IRQ & 0 ; 28 DMA channel 5 & 0 & IRQ & 0 ; 29 DMA channel 6 & 0 & IRQ & 0 ; 30 DMA channel 7 & 0 & IRQ & 0 ; 31 PCI & PIRQ_Chain - (PodDesp_Link-PodDesp_R12Val) ;PCI shares PIRQ_Chain with Podule & PCIIRQ_Despatch & 0 ; 32 PLX (PCI controller) & 0 & IRQ & 0 ; 33 IDE B & 0 & IRQ & 0 ; 34 Audio 0 & 0 & IRQ & 0 ; 35 Audio 1 & 0 & IRQ & 0 ; 36 Timer 2 & 0 & IRQ & 0 ; 37 Timer 3 & 0 & IRQ & 0 ; 38 Timer 4 & 0 & IRQ & 0 ; 39 Timer 5 & 0 & IRQ & 0 ; 40 Serial 2 & 0 & IRQ & 0 ; 41 IR & 0 & IRQ & 0 ; terminate ; nothing is interrupting, which is impossible: just call IRQ2V anyway & 0 & IRQ & 0 IOMD2_Devices_end ASSERT IOMD2_Devices_end-IOMD2_Devices = (IOMD2_NoInterrupt+1)*4*3 ; DMA structures (processing bits from DMAREQ) ; these are similar to IRQ structures, see IRQ comments for description ; IOMD2_DMA_HalfSize * 5 ; IOMD2_DMA_HalfMask1 DCD (1:SHL: 0)+(1:SHL: 1)+(1:SHL: 2)+(1:SHL: 3)+(1:SHL: 4) ; IOMD2_DMACheck = 32- 0 ;DMA channel 0 = 32- 1 ;DMA channel 1 = 32- 2 ;DMA channel 2 = 32- 3 ;DMA channel 3 = 32- 4 ;DMA sound channel (VIDC sound) = 32- 8 ;DMA channel 4 = 32- 9 ;DMA channel 5 = 32-10 ;DMA channel 6 = 32-11 ;DMA channel 7 = 32-31 ;fail safe terminator is bit 31 (always set) ; IOMD2_DMADevice = 16*3 ;DMA channel 0 = 17*3 ;DMA channel 1 = 18*3 ;DMA channel 2 = 19*3 ;DMA channel 3 = 20*3 ;DMA sound channel (VIDC sound) = 27*3 ;DMA channel 4 = 28*3 ;DMA channel 5 = 29*3 ;DMA channel 6 = 30*3 ;DMA channel 7 = IOMD2_NoInterrupt*3 ;failsafe terminator (max real device is IOMD2_NoInterrupt-1) ALIGN ;IRQ structures (processing bits from INTREQ) ; ;to speed things up a little, two-stage binary chop to give cut worst case search by factor of 4 ;this is stored just before table IOMD2_IRQCheck for extra speed (including cache friendliness) ;the bits set *must* agree with IOMD2_IRQCheck table, which *must* follow immediately after ; IOMD2_IRQ_HalfSize * 10 ;ten devices in half mask IOMD2_IRQ_QuartSize * 5 ;five devices in each quarter mask ; IOMD2_IRQ_HalfMask1 ;1st half DCD (1:SHL:26)+(1:SHL:25)+(1:SHL: 1)+(1:SHL: 2)+(1:SHL: 7)+(1:SHL:29)+(1:SHL:28)+(1:SHL: 0)+(1:SHL: 3)+(1:SHL: 4) IOMD2_IRQ_QuartMask1 ;1st quarter DCD (1:SHL:26)+(1:SHL:25)+(1:SHL: 1)+(1:SHL: 2)+(1:SHL: 7) IOMD2_IRQ_QuartMask3 ;3rd quarter DCD (1:SHL:27)+(1:SHL:21)+(1:SHL:18)+(1:SHL:16)+(1:SHL: 6) ; ;table is in order of decreasing priority, entries are amount to LSL shift appropriate ;bit of r0 into PSR C flag (allows fast test with single byte table entries) ; IOMD2_IRQCheck = 32-26 ;serial 1 (bit 26 of r0) = 32-25 ;serial 2 = 32- 1 ;podule = 32- 2 ;PCI = 32- 7 ;PLX = 32-29 ;keyboard = 32-28 ;mouse = 32- 0 ;podule FIQ downgrade (not fully compatible on IOMD2?) = 32- 3 ;IDE A = 32- 4 ;IDE B = 32-27 ;floppy = 32-21 ;FIQ downgrade (permanent interrupt, via special use of Timer5) = 32-18 ;Timer2 (currently only allow one new timer, to avoid device proliferation) = 32-16 ;Timer0 = 32- 6 ;VSync = 32-17 ;Timer1 = 32- 8 ;Audio 0 = 32- 9 ;Audio 1 = 32- 5 ;floppy index = 32-11 ;parallel = 32-31 ;fail safe terminator is bit 31 (always set) ; ;table is in same order as IOMD2_IRQCheck, maps found index to external ;device number *3 (for easier lookup of IOMD2_Devices) ;this table *must* immediately follow IOMD2_IRQCheck ; IOMD2_IRQDevice = 10*3 ;serial 1 (device 10) = 40*3 ;serial 2 = 13*3 ;podule = 31*3 ;PCI = 32*3 ;PLX = 14*3 ;keyboard = 15*3 ;mouse = 8*3 ;podule FIQ downgrade = 9*3 ;IDE A = 33*3 ;IDE B = 12*3 ;floppy = 7*3 ;FIQ downgrade = 36*3 ;Timer2 = 5*3 ;Timer0 = 3*3 ;VSync = 6*3 ;Timer1 = 34*3 ;Audio 0 = 35*3 ;Audio 1 = 2*3 ;floppy index = 0*3 ;parallel = IOMD2_NoInterrupt*3 ;failsafe terminator (max real device is IOMD2_NoInterrupt-1) ALIGN IOMD2_DefaultIRQ1Vcode_end ASSERT IOMD2_DefaultIRQ1Vcode_end - IOMD2_DefaultIRQ1Vcode <= DefIRQ1Vspace ] ;IOMD2Support ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Specialist despatchers for podules and PCI cards ^ 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 ; PCI cards ; In r12 = PIRQ_Chain - (PodDesp_Link-PodDesp_R12Val) from despatcher PCIIRQ_Despatch ROUT 01 LDR r12, [r12, #PodDesp_Link-PodDesp_R12Val] LDMIA r12!, {r1, r2} ; address and mask LDR r1, [r1] ;note, word access for PCI (byte for podule) ANDS r1, r1, r2 BEQ %BT01 LDMIA r12, {r12, pc} ; Podules ; 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] ANDS r1, r1, 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 lr, =IOMD_NoInterrupt LDR lr, [lr] LDR r0, [sp] CMP r0, lr BGE DV_Fail_NaffDevNo CMP r0, #Podule_DevNo [ IOMD2Support CMPNE r0, #IOMD2_PCI_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 ASSERT IOMD_Devices = IOMD_DefaultIRQ1Vcode+8 LDR lr, =IOMD_DefaultIRQ1Vcode LDR r1, [lr] LDR r3, [lr,#8] SUB r3, r3, r1 ; offset from code start to devices table LDR r1, =DefaultIRQ1V ; code start in RAM ADD r1, r1, r3 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 BL TranslateError B DV_Exit MakeErrorBlock NaffDevNo ;handles podule or PCI claim ; 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 [ IOMD2Support CMPNE r0, #IOMD2_PCI_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 LDR lr, =IOMD_NoInterrupt LDR lr, [lr] CMP r0, lr BGE DV_Fail_NaffDevNo TEQP pc, #SVC_mode + I_bit ; IRQs off while holding context CMP r0, #Podule_DevNo [ IOMD2Support CMPNE r0, #IOMD2_PCI_DevNo ] CMPNE r0, #PFIQasIRQ_DevNo BEQ PoduleChainRelease ADD r0, r0, r0, LSL #1 ; *3 ASSERT IOMD_Devices = IOMD_DefaultIRQ1Vcode+8 LDR lr, =IOMD_DefaultIRQ1Vcode LDR r12, [lr] LDR r11, [lr,#8] SUB r11, r11, r12 ; offset from code start to devices table LDR r12, =DefaultIRQ1V ; code start in RAM ADD r12, r12, r11 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 BL TranslateError 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 ;handles podule or PCI release ; PoduleChainRelease CMP r0, #Podule_DevNo [ IOMD2Support CMPNE r0, #IOMD2_PCI_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" MOV r10, #IrqV BL CallVector Pull "r10, pc" ; return: someone will always claim it. ; ***************************************************************************** ; 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 *171,/512: accurate for 0..511 result ... NOIRQ ROUT [ IOMD1Support ASSERT IOMD1_NoInterrupt < 512 ] [ IOMD2Support ASSERT IOMD2_NoInterrupt < 512 ] MOV r1, #171 MUL r12, r0, r1 MOV r12, r12, LSR #9 ;r12 := devno NOIRQ_eh MOV r0, #IOC LDRB r0, [r0, #IOMD_ID0] [ IOMD1Support TEQ r0, #IOMD_Original :AND: &FF TEQNE r0, #IOMD_7500 :AND: &FF TEQNE r0, #IOMD_7500FE :AND: &FF BEQ NOIRQ_IOMD1 ] [ IOMD2Support TEQ r0, #IOMD_IOMD2 :AND: &FF BEQ NOIRQ_IOMD2 ] B NOIRQ_eh ;deliberate panic hang up [ IOMD1Support NOIRQ_IOMD1 ; Cope with interrupts from IRQC registers ; ; 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 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 03 ADD r0, r0, #IOC MOV r1, #1 MOV r1, r1, LSL r12 ; bit to clear 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 ] ;IOMD1Support [ IOMD2Support NOIRQ_IOMD2 ADR r0, NOIRQ_IOMD2table LDRB r0, [r0, r12] AND r12, r0, #31 ;bit number MOV r1, #1 MOV r1, r1, LSL r12 ;bit mask MOV r12, r0, LSR #5 ;encoded register CMP r12, #4 ADDLO pc,pc,r12,LSL #2 ;dispatch Pull pc,,^ ;4..7 means do nothing (reserved) B NOIRQ_IOMD2_dma ;0 means DMA register B NOIRQ_IOMD2_int ;1 means INT register B NOIRQ_IOMD2_SMI1 ;2 means combo SMI1 register B NOIRQ_IOMD2_SMI2 ;3 means combo SMI2 register ; NOIRQ_IOMD2_dma MOV r12, #IOC LDR r0, [r12, #IOMD2_DMAENBL] BIC r0, r0, r1 STR r0, [r12, #IOMD2_DMAENBL] Pull pc,,^ ; NOIRQ_IOMD2_int MOV r12, #IOC MOV lr, pc TEQP pc, #IRQ_mode+I_bit+F_bit ;FIQs off to mess with INTENBL LDR r0, [r12, #IOMD2_INTENBL] BIC r0, r0, r1 STR r0, [r12, #IOMD2_INTENBL] TEQP lr, #0 STR r1, [r12, #IOMD2_INTREQ] Pull pc,,^ ; NOIRQ_IOMD2_SMI1 LDR r12,=SMI_ComboBase MOV r0, #SMI_enable1 STR r0, [r12, #SMI_selectreg] LDRB r0, [r12, #SMI_accessreg] BIC r0, r0, r1 STRB r0, [r12, #SMI_accessreg] Pull pc,,^ ; NOIRQ_IOMD2_SMI2 LDR r12,=SMI_ComboBase MOV r0, #SMI_enable2 STR r0, [r12, #SMI_selectreg] LDRB r0, [r12, #SMI_accessreg] BIC r0, r0, r1 STRB r0, [r12, #SMI_accessreg] Pull pc,,^ ; ;indexed by device number, 1 byte per entry ;value in bits 0..4 is bit number of a register (0 to 31) ;value in bits 5..7 is encoded register: ; 0 = IOMD2 DMAENBL ; 1 = IOMD2 INTENBL (and clear in INTREQ, in case edge sensitive) ; 2 = Combo SMI enable1 ; 3 = Combo SMI enable2 ; 4..7 reserved (do nothing) ; NOIRQ_IOMD2table ;dev name bit register = (1 :SHL: 5) + 11 ; 0 parallel 11 INTENBL = (7 :SHL: 5) + 0 ; 1 - = (1 :SHL: 5) + 5 ; 2 floppy index 5 INTENBL = (1 :SHL: 5) + 6 ; 3 vsync 6 INTENBL = (7 :SHL: 5) + 0 ; 4 - = (1 :SHL: 5) + 16 ; 5 Timer0 16 INTENBL = (1 :SHL: 5) + 17 ; 6 Timer1 17 INTENBL = (1 :SHL: 5) + 21 ; 7 FIQ downgrade 21 INTENBL (uses Timer5) = (1 :SHL: 5) + 0 ; 8 PFIQ downgrade 0 INTENBL = (1 :SHL: 5) + 3 ; 9 IDE A 3 INTENBL = (2 :SHL: 5) + 3 ; 10 serial 1 3 SMI1 = (7 :SHL: 5) + 0 ; 11 - = (2 :SHL: 5) + 4 ; 12 floppy 4 SMI1 = (1 :SHL: 5) + 1 ; 13 PIRQ 1 INTENBL = (3 :SHL: 5) + 1 ; 14 keyboard 1 SMI2 = (3 :SHL: 5) + 0 ; 15 mouse 0 SMI2 = (0 :SHL: 5) + 0 ; 16 DMA chan 0 0 DMAENBL = (0 :SHL: 5) + 1 ; 17 DMA chan 1 1 DMAENBL = (0 :SHL: 5) + 2 ; 18 DMA chan 2 2 DMAENBL = (0 :SHL: 5) + 3 ; 19 DMA chan 3 3 DMAENBL = (0 :SHL: 5) + 4 ; 20 Sound DMA 0 4 DMAENBL = (7 :SHL: 5) + 0 ; 21 - = (7 :SHL: 5) + 0 ; 22 - = (7 :SHL: 5) + 0 ; 23 - = (7 :SHL: 5) + 0 ; 24 - = (7 :SHL: 5) + 0 ; 25 - = (7 :SHL: 5) + 0 ; 26 - = (0 :SHL: 5) + 8 ; 27 DMA chan 4 8 DMAENBL = (0 :SHL: 5) + 9 ; 28 DMA chan 5 9 DMAENBL = (0 :SHL: 5) + 10 ; 29 DMA chan 6 10 DMAENBL = (0 :SHL: 5) + 11 ; 30 DMA chan 7 11 DMAENBL = (1 :SHL: 5) + 2 ; 31 PCI 2 INTENBL = (1 :SHL: 5) + 7 ; 32 PLX 7 INTENBL = (1 :SHL: 5) + 4 ; 33 IDE B 4 INTENBL = (1 :SHL: 5) + 8 ; 34 Audio 0 8 INTENBL = (1 :SHL: 5) + 9 ; 35 Audio 1 9 INTENBL = (1 :SHL: 5) + 18 ; 36 Timer2 18 INTENBL = (1 :SHL: 5) + 19 ; 37 Timer3 19 INTENBL = (1 :SHL: 5) + 20 ; 38 Timer4 20 INTENBL = (1 :SHL: 5) + 21 ; 39 Timer5 21 INTENBL = (2 :SHL: 5) + 2 ; 40 serial 2 2 SMI1 = (3 :SHL: 5) + 2 ; 41 IR 2 SMI2 ALIGN ] ;IOMD2Support ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; 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) ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ALIGN 32 ;IOMD2 has a problem that Timer IRQ cannot be cleared until after timer has ;counted away from 0 (up to 0.5us). This is unlikely to be a problem, because ;of the time to respond and dispatch the interrupt, but has been seen on slow ;FPGA at least. TickOne is now rearranged to delay IRQ clear as long as ;possible, and to ensure IRQ is clear. These changes should be harmless ;on IOMD1 TickOne ROUT Push r14 ;Counter for VDU CTRL timing ; LDRB R0, CentiCounter SUBS R0, R0, #1 STRCSB R0, CentiCounter ; decrement if not zero ;Real time ; 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 ;System clock ; 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 ;Clear interrupt source (must do this before any possible IRQ re-enable) ; MOV R3, #IOC 10 MOV R0, #timer0_bit STRB R0, [R3, #IOCIRQCLRA] ; clear timer 0 interrupt LDRB R0, [R3, #IOCIRQREQA] TST R0, #timer0_bit ; wait for clear, just in case (IOMD2 workaround) BNE %BT10 ;Interval timer, and keyboard timing (IRQs may be re-enabled temporarily ;eg. by Event client) ; 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" ;Ticker stuff ; Push R10 MOV R10, #TickerV ; call 100Hz vector BL CallVector ; IRQ's still disabled BL ProcessTickEventChain ; Re-enables IRQs Pull "R10,PC" ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Process VSync IRQ device ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 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 [ ChocolateScreen :LAND: ARMSASupport ; ;after all vsync stuff (except flashing colours), screen cleaner stuff if enabled ;(will only be enabled for StrongARM) ; MOV R2,#0 LDR R0,[R2,#ARMA_Cleaner_status] TST R0,#ACS_SCdisable:OR:ACS_SCsuspend BLEQ VsyncSAScreenClean ; do the VSC jazz if not disabled or suspended ] 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" [ ChocolateScreen :LAND: ARMSASupport ;entry: R0 = current ARMA_Cleaner_status, R2=0, R0,R1 trashable, ARM is StrongARM ; VsyncSAScreenClean ROUT ; ; 1) if VSC countdown is zero but dirty screen is flagged (VIDMRD), we want to restart countdown ; 2) if VSC countdown is now zero, we do nothing (no clean pending) ; 3) if VSC countdown is now non-zero, then we want to decrement countdown ; 4) if decremented countdown would hit zero, we check for lockout by semaphore, and hold countdown at 1 if so ; 5) if countdown has hit 0 we do a screen clean, and reset everything for next time ; ;1) TST R0,#ACS_HardVIDMRD ;check for h/w VIDMRD presence BEQ %FT01 MOV R1,#IOMD_Base LDR R1,[R1,#IOMD2_VIDMRD] ;this also clears h/w flag TST R1,#1 ORRNE R0,R0,#ACS_SoftVIDMRD STRNE R0,[R2,#ARMA_Cleaner_status] ;we *must* stickily reflect transient h/w flag in SoftVIDMRD 01 AND R1,R0,#ACS_VSCpending_MASK TEQ R1,#ACS_SoftVIDMRD ;EQ if countdown currently zero but SoftVIDMRD set ANDEQ R1,R0,#ACS_VSClazy_MASK ORREQ R0,R0,R1,LSR #(ACS_VSClazy_SHIFT-ACS_VSCcountdown_SHIFT) ;restart countdown (VSC countdown := VSC lazy) ;2) TST R0,#ACS_VSCcountdown_MASK MOVEQ PC,LR ;no pending VSC if VSC countdown is 0 ;3) SUB R0,R0,#1:SHL:ACS_VSCcountdown_SHIFT ;decrement VSC countdown TST R0,#ACS_VSCcountdown_MASK STRNE R0,[R2,#ARMA_Cleaner_status] MOVNE PC,LR ;nothing to do yet if VSC countdown has not reached zero ;4) TST R0,#ACS_NSCsemaphore:OR:ACS_SCsemaphore ORRNE R0,R0,#1:SHL:ACS_VSCcountdown_SHIFT STRNE R0,[R2,#ARMA_Cleaner_status] MOVNE PC,LR ;if NSC or SC semaphore set, do nothing now but hold VSC countdown at 1 ;5) EOR R0,R0,#ACS_SCflipflop ;next screen cleaner area ORR R0,R0,#ACS_SCsemaphore BIC R0,R0,#ACS_VSCpending_MASK ;clear this since we are about to clean (appropriate) cache STR R0,[R2,#ARMA_Cleaner_status] TST R0,#ACS_MiniDataCache MOVEQ R2,#16*1024 ;16k data cache size MOVNE R2,#1024 ;1k mini data cache size AND R0,R0,#ACS_SCflipflop ;extract SC flipflop bit LDR R1,=ARMA_ScreenCleaners_address ADD R1,R1,R0,LSR #ACS_SCflipflop_SHIFT-14 ;start address for clean (LSR -14 since 16k area size) ADD R2,R1,R2 ;end address (exclusive, 16k or 1k cache size) 02 LDR R0,[R1],#32 ;read next cleaner line into data cache (triggers 8-word line fill) LDR R0,[R1],#32 LDR R0,[R1],#32 LDR R0,[R1],#32 CMP R1,R2 BLO %BT02 ;until done ;write buffer can be allowed to drain on its own (this is not a code- or remap- critical clean) MOV R2,#0 LDR R0,[R2,#ARMA_Cleaner_status] TST R0,#ACS_HardVIDMRD ARMA_read_MMUdomain R1, EQ BICEQ R1,R1,#&C ARMA_write_MMUdomain R1, EQ ;reset screen (domain 1) to fault fo VIDMRD emulation, if necessary BIC R0,R0,#ACS_SCsemaphore STR R0,[R2,#ARMA_Cleaner_status] MOV PC,LR ] ;ChocolateScreen ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ END