; IOC Timer1 is reserved for the user/application programs

; It is not as easy to use as the 6522 on the BBC, as one of the IOC
; registers controlling it is also used by FIQ processes, so care must be
; exercised when updating it. Also, you can only access it from a privileged
; mode (normally SVC or IRQ) and not from BASIC, for example.

; IOC timers decrement at 2 MHz, and generate an interrupt (if enabled) when
; crossing zero; they then reload from the latched counter value.

; Accesses to IOC must always be done using LDRB/STRB and not LDR/STR due
; to the way IOC sits on the 16 bit wide I/O bus

IOC     *       &03200000

; Offsets from IOC base

IOCIRQSTAA * &10        ; IRQ A Status
IOCIRQREQA * &14        ;       Requesting
IOCIRQCLRA * &14        ;       Clear
IOCIRQMSKA * &18        ;       Mask

Timer1LL   * &50        ; Timer 1 Write low input latch
Timer1LH   * &54        ;         Write high input latch
Timer1GO   * &58        ;         Load count from input latch
Timer1CL   * &50        ;         Read low output latch
Timer1CH   * &54        ;         Read high output latch
Timer1LR   * &5C        ;         Load output latch from count

; Bit position in IRQ registers

timer1_bit * 1 :SHL: 6  ; Timer 1 crossing zero (event)

; Device number for OS_ClaimDeviceVector

Timer1_DevNo    * 6

I_bit   *       1 :SHL: 27
F_bit   *       1 :SHL: 26


; You can set the counter and reload value at any time:

StartTimer
        MOV     r3, #IOC
        MOV     r2, r1, LSR #8          ; Hi byte
        STRB    r1, [r3, #Timer1LL]     ; Store value into input latches
        STRB    r2, [r3, #Timer1LH]
        STRB    r3, [r3, #Timer1GO]     ; Load counter from input latches
                                        ; (source register irrelevant)

; You can read the counter at any time:

ReadTimer
        MOV     r3, #IOC
        STRB    r3, [r3, #Timer1LR]     ; Store counter into output latches
                                        ; (source register irrelevant)
        LDRB    r2, [r3, #Timer1CL]     ; Load value from output latches
        LDRB    r0, [r3, #Timer1CH]
        ADD     r2, r2, r0, LSL #8      ; r0 := counter


; This is the nasty bit ...

EnableTimerIRQ
        MOV     r3, #IOC
        MOV     r4, pc
        ORR     r5, r4, #I_bit + F_bit  ; ALL interrupts off as FIQ process
        TEQP    r5, #0                  ; also modifies MSKA to downgrade
        LDRB    r0, [r3, #IOCIRQMSKA]   ; some FIQs into IRQs
        ORR     r0, r0, #timer1_bit
        STRB    r0, [r3, #IOCIRQMSKA]
        TEQP    r4, #0                  ; Restore interrupt state

; To disable timer IRQ's, use BIC rather than ORR

; Before you call EnableTimerIRQ, you should install yourself on the device
; vector for timer 1 by executing the following code

Init
        MOV     r0, #Timer1_DevNo       ; r0 = device number
        ADR     r1, IRQHandler          ; r1 -> routine
        MOV     r2, r12                 ; r2 = value in r12 on entry to IRQHandler
        SWI     XOS_ClaimDeviceVector
        BVS     Fail

; When your module or application dies, it must remove itself from the
; device vector by calling XOS_ReleaseDeviceVector with the same parameters
; as above.

; The interrupt handler should look like the following

IRQHandler
        STMFD   r13!, {tempregs, r11, r14}
        MOV     r11, #IOC
        MOV     r14, #timer1_bit
        STRB    r14, [r11, #IOCIRQCLRA] ; Need to clear the IRQ as it's latched

; ... whatever you do here, keep it snappy (please)

        LDMFD   r13!, {tempregs, r11, pc} ; return from routine

