; 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.
;
; > Sources.PMF.KbdDrA1

; Archimedes keyboard driver.

; ***********************************
; ***    C h a n g e   L i s t    ***
; ***********************************

; Date      Who Description
; ----      --- -----------
; 24-Feb-93 SMC Split from file "key".
;               Use generic keyboard/mouse interfaces.

        [ PollMouse
K1ack   *       K1sack
        |
K1ack   *       K1smak
        ]

        MACRO
$lab    RXonTXoff  $reg, $cond
$lab
        LDR$cond.B $reg, IRQMaskB
        BIC$cond   $reg, $reg, #KARTTxBit
        ORR$cond   $reg, $reg, #KARTRxBit
        STR$cond.B $reg, IRQMaskB
        MEND

        MACRO
$lab    TXonRXoff  $reg, $cond
$lab
        LDR$cond.B $reg, IRQMaskB
        BIC$cond   $reg, $reg, #KARTRxBit
        ORR$cond   $reg, $reg, #KARTTxBit
        STR$cond.B $reg, IRQMaskB
        MEND

        MACRO
$lab    RXon       $reg, $cond
$lab
        LDR$cond.B $reg, IRQMaskB
        ORR$cond   $reg, $reg, #KARTRxBit
        STR$cond.B $reg, IRQMaskB
        MEND

        MACRO
$lab    TXon       $reg, $cond
$lab
        LDR$cond.B $reg, IRQMaskB
        ORR$cond   $reg, $reg, #KARTTxBit
        STR$cond.B $reg, IRQMaskB
        MEND

        MACRO
$lab    TXoff      $reg, $cond
$lab
        LDR$cond.B $reg, IRQMaskB
        BIC$cond   $reg, $reg, #KARTTxBit
        STR$cond.B $reg, IRQMaskB
        MEND

        GBLS    irqregs
irqregs SETS    """R4-R10, PC"""

; *****************************************************************************
;
;       Start of code

ArthurKeyDriver

; *****************************************************************************
;
;       Initialise driver.
;
A1KeyInit
; Initialise the baud rate generator

        MOV     R12, #IOC
        MOV     R0, #1
        STRB    R0, Timer3Low
        MOV     R0, #0
        STRB    R0, Timer3High
        STRB    R0, Timer3Go

        STRB    R0, KARTTx              ; Write dummy byte

        MOV     R0, #&800
10
        SUBS    R0, R0, #1              ; copy Jon's loop
        BNE     %BT10

        LDRB    R0, KARTRx              ; Read dummy byte

 [ AssemblePointerV
        MOV     r0, #PointerV
        ADR     r1, A1Pointer
        Push    lr
        SWI     OS_Claim
        Pull    lr
 ]

 [ AssembleKEYV
        MOV     r0, #KEYV
        ADR     r1, A1KeyVec
        MOV     r2, r12
        Push    lr
        SWI     OS_Claim
        Pull    pc
 |
        MOV     pc, lr
 ]

; *****************************************************************************
;
;       Reset keyboard hardware.
;
A1Reset
        MOV     R0, #HRDRESET
        STRB    R0, ResetState

        ASSERT  HRDRESET = &FF
        STRB    R0, KeyRow              ; no key being received
        STRB    R0, Reply
        STRB    R0, LastKbId
        STRB    R0, KbIdHalf
        STRB    R0, RequestLED

        MOV     R0, #K1rqid
        STRB    R0, RequestKbId

        MOV     R0, #0
        STRB    R0, JustGotKbId
        STRB    R0, MouseCount          ; start with an X coordinate
        STRB    R0, SPDRec
        STRB    R0, RequestSPD
        STRB    R0, RequestMouse
 [ AssemblePointerV
        STR     r0, MouseXCount
        STR     r0, MouseYCount
 ]

        TXonRXoff       R0              ; enable Tx IRQ, disable Rx IRQ

        MOV     pc, lr

; *****************************************************************************
;
;       Handle enable and update LEDs (r1=state (bit 2=scroll, 1=num, 0=ctrl)).
;
;       In:     r0 = reason code 3 or 4
;               r1 = state (bit 2=scroll lock, 1=num lock, 0=caps lock) if r0=3
;               r12 = IOC
;
A1KeyVec
        TEQ     r0, #3                  ; if not set LED
        TEQNE   r0, #4                  ;     and not enable then
        MOVNE   pc, lr                  ;   pass it on

        Push    "r0,r1,r11,lr"

        MOV     r11, #KeyWorkSpace

        TEQ     r0, #4                  ; if enable then
        MOVEQ   r0, #0
        STREQB  r0, JustGotKbId         ;   clear flag
        RXon    r0, EQ                  ;   enable RX IRQs
        Pull    "r0,r1,r11,pc",EQ       ;   and pass on call

        LDRB    r0, KbId
        CMP     r0, #1                  ; if id >= 1 then new (C=1)
        BCS     %FT10

        TST     r1, #1
        MOVEQ   r1, #LEDOFF
        MOVNE   r1, #LEDON
10
        STRB    r1, RequestLED
        TXon    r0

        Pull    "r0,r1,r11,pc"

 [ AssemblePointerV

; *****************************************************************************
;
;       A1Pointer - Return mouse movements on PointerV poll.
;
;       In:     r0 = reason code 0
;               r1 = pointer device type (must be 0 for us)
;       Out:    r2 = signed 32-bit X movement
;               r3 = signed 32-bit Y movement
;
A1Pointer
        TEQ     r0, #0                  ; If not poll
        TEQEQ   r1, #0                  ;     or not type 0 then
        MOVNE   pc, lr                  ;   pass on call.

        LDR     r2, MouseXCount
        STR     r0, MouseXCount
        LDR     r3, MouseYCount
        STR     r0, MouseYCount

        Pull    pc                      ; Claim call.
 ]

; *****************************************************************************
;
;       IRQ routine
;
; in:   R2 = IOC request B flags
;       R0-R3, R11, R12 already saved, R14 irrelevant


        [ AssemblingArthur
IrqRx   ROUT
        Push    "R4-R10, R14"           ; stack regs if new MOS IRQ vector
        |
KeyIrq  ROUT
        TST     R2, #KARTTxBit          ; transmit empty ?
        BNE     IrqTx
        Push    "R4-R10"
        MOV     R12, #IOC               ; already set up in new IRQ scheme
        ]
        MOV     R11, #KeyWorkSpace

; Keyboard receive interrupt

; We now have to wait around for a period of 16 microseconds (or so they say)
; because of the hardware design.

; This is doubly annoying because I have no idea what speed this processor is
; going at, so I don't know how many S-cycles this is, and there aren't enough
; hardware timers around to waste one doing this thankless task.

; In addition, because I am on the IRQ vector, the other IRQ users have
; probably wasted at least 16 microseconds anyway - at least the code seems
; to work perfectly well without this delay loop.

; Nevertheless, until we can come up with a better solution, I shall do a
; delay of (about) 16*8 S-cycles.
;

        MOV     R0, #16*8/5             ; delay for an unspecified period
IrqRxDelayLoop
        SUBS    R0, R0, #1              ; this breaks my heart,
        BNE     IrqRxDelayLoop          ; it really does !

        LDRB    R0, KARTRx              ; get data byte
        LDRB    R1, ResetState          ; and what we sent last

        CMP     R0, #K1rak2             ; is it a reset thingy ?
        BCS     ProcessReset            ; [yes, so check it]

        CMP     R1, #K1rak2             ; are we resetting anyway ?
        BCS     IrqBadRx                ; if so then bad reset

        AND     R2, R0, #&F0            ; get reason code

        LDRB    R1, LastKbId            ; get last keyboard ID
        TEQ     R1, #&FF                ; is it valid yet ?
        BNE     ValidKbId               ; [yes, so we know what to expect]

        TEQ     R2, #IDTYPE             ; is it old keyboard id
        BEQ     IsOldKeyboard           ; [is old keyboard]

        BIC     R2, R2, #K1kbidmask     ; check for new keyboard id
        TEQ     R2, #K1kbid
        BNE     IrqBadRx                ; not a keyboard id, so reset

        AND     R1, R0, #K1kbidmask     ; get relevant bits
 [ AssembleA1KeyHandler
        ADRL    R0, NewKeyStruct
 ]
        B       AcknowledgeId

IsOldKeyboard
        AND     R0, R0, #&0F            ; get ID part
        LDRB    R1, KbIdHalf
        TST     R1, #&80
        STRNEB  R0, KbIdHalf            ; got half of keyboard id
        MOVNE   R0, #K1nack
        BNE     IrqRxAck

        ORR     R1, R1, R0, LSL #4      ; get full keyboard id
        MOV     R0, #&FF
        STRB    R0, KbIdHalf
 [ AssembleA1KeyHandler
        ADRL    R0, OldKeyStruct
 ]

AcknowledgeId
 [ AssembleA1KeyHandler
        LDRB    R8, LastKbId            ; get last keyboard id
        TEQ     R8, R1                  ; is it same
        STRNE   R0, KeyVec              ; if different, set up our handler
 ]
        MOV     R0, #&FF
        STRB    R0, JustGotKbId         ; tell TX handler to disable interrupts until KEYV enable call

 [ AssembleKEYV
        MOV     r0, #0
        MOV     r10, #KEYV
        BL      CallVector
 |
        BL      GotKbId
 ]

        B       IrqRxAckScan            ; send ack

; R1 = keyboard ID - now dispatch code

ValidKbId
        TEQ     R1, #0
        BNE     NewKeyboardDispatch

OldKeyboardDispatch
        TST     R0, #MMOVED             ; is it mouse data ?
        BNE     ProcessOldMouseData
        TEQ     R2, #KEYDOWN            ; is it key down ?
        BEQ     ProcessOldKeyDown
        TEQ     R2, #KEYUP              ; is it key up ?
        BEQ     ProcessOldKeyUp
        TEQ     R2, #SPDDONE            ; is it SPD data ?
        BEQ     ProcessOldSPDData
        B       IrqBadRx                ; spurious

NewKeyboardDispatch
        TST     R2, #K1notmousedata     ; is it mouse data ?
        BEQ     ProcessNewMouseData
        TEQ     R2, #K1kdda             ; is it key down ?
        BEQ     ProcessNewKeyDown
        TEQ     R2, #K1kuda             ; is it key up ?
        BEQ     ProcessNewKeyUp
        TEQ     R2, #K1pdat             ; is it SPD data ?
        BEQ     ProcessNewSPDData
        B       IrqBadRx                ; spurious


; *****************************************************************************
;
;       ProcessReset - Process reset code from keyboard
;
; in:   R0 = code from keyboard
;       R1 = ResetState

ProcessReset ROUT

; Check sequencing

        TEQ     R1, R0                  ; is reply what was expected
        BNE     IrqBadRx                ; no, so reset

; Now continue the sequence

        TEQ     R1, #K1rak2             ; end of sequence ?
        MOVEQ   R1, #K1nack             ; then send a nack
        SUBNE   R1, R1, #1              ; else next thing to go
        STRB    R1, ResetState          ; store back

        TXonRXoff R0

        Pull    $irqregs

IrqBadRx

; Restart the reset sequence

        BL      A1Reset
        Pull    $irqregs

; *****************************************************************************

ProcessOldSPDData
ProcessNewSPDData
        LDRB    R1, SPDRec
        SUBS    R1, R1, #1
        STRCSB  R1, SPDRec                      ; dec number to go (if not 0)

        LDRCS   R1, SPDoutput
        MOVCS   R1, R1, LSR #4
        ORRCS   R1, R1, R0, LSL #28             ; put in new data
        STRCS   R1, SPDoutput

        B       IrqRxAckScan

; *****************************************************************************

ProcessOldMouseData                     ; R0 = 01xx xxxx
        TST     R0, #&20                ; get sign bit of data (bit 5)
        BICEQ   R0, R0, #&40            ; move to bit 6 (where it is on new)
ProcessNewMouseData
        LDRB    R1, MouseCount
        ADR     R2, MouseDelta
        STRB    R0, [R2, R1]            ; no need to clear top bit

        EORS    R1, R1, #1              ; move to other coordinate
        STRB    R1, MouseCount

        MOVNE   R0, #K1back
        BNE     IrqRxAck

        LDRB    R3, [R2, #1]            ; get delta Y
        MOV     R3, R3, LSL #25         ; sign extend it
        MOV     R3, R3, ASR #25

        LDRB    R2, [R2]                ; get delta X
        MOV     R2, R2, LSL #25         ; sign extend it
        MOV     R2, R2, ASR #25

 [ AssemblePointerV
        LDR     r0, MouseXCount
        ADD     r0, r0, r2
        STR     r0, MouseXCount
        LDR     r0, MouseYCount
        ADD     r0, r0, r3
        STR     r0, MouseYCount
 |
        BL      ProcessMouseXY
 ]

        B       IrqRxAckScan

; *****************************************************************************

; in:   R1 = keyboard id

ProcessOldKeyDown
ProcessNewKeyDown ROUT
        LDRB    R2, KeyRow
        TEQ     R2, #&FF                ; have we had a row already ?
        STREQB  R0, KeyRow              ; no so store row
        MOVEQ   R0, #K1back
        BEQ     IrqRxAck                ; and acknowledge Rx

        EOR     R3, R0, R2              ; test if save movement type
        TST     R3, #&F0
        BNE     IrqBadRx                ; not same, so reset

        AND     R0, R0, #&0F            ; get new data
        AND     R2, R2, #&0F            ; and row data

        TEQ     R1, #0
        ORREQ   R2, R2, R0, LSL #4      ; old keyboard number
        ORRNE   R2, R0, R2, LSL #4      ; new key number

        MOV     R0, #&FF
        STRB    R0, KeyRow              ; reset 'had row' flag

        MOV     r0, #2                  ; indicate key down
        MOV     r1, r2
 [ AssembleKEYV
        MOV     r10, #KEYV
        BL      CallVector
        MOV     r12, #IOC
 |
        BL      GotKey
 ]

IrqRxAckScan

; Re-enable Tx interrupts and queue an acknowledge

        MOV     R0, #K1ack              ; either sack or smak as appropriate
IrqRxAck
        STRB    R0, Reply
        TXonRXoff       R0
        Pull    $irqregs                ; claimed irq, so grab link and PC

; *****************************************************************************

; in:   R1 = keyboard id

ProcessOldKeyUp
ProcessNewKeyUp ROUT
        LDRB    R2, KeyRow
        TEQ     R2, #&FF                ; have we had a row already ?
        STREQB  R0, KeyRow              ; no so store row
        MOVEQ   R0, #K1back
        BEQ     IrqRxAck                ; and acknowledge Rx

        EOR     R3, R0, R2              ; test if save movement type
        TST     R3, #&F0
        BNE     IrqBadRx                ; not same, so reset

        AND     R0, R0, #&0F            ; get new data
        AND     R2, R2, #&0F            ; and row data

        TEQ     R1, #0
        ORREQ   R2, R2, R0, LSL #4      ; old key number
        ORRNE   R2, R0, R2, LSL #4      ; new key number

        MOV     R0, #&FF
        STRB    R0, KeyRow              ; reset 'had row' flag

        MOV     r0, #1                  ; indicate key up
        MOV     r1, r2
 [ AssembleKEYV
        MOV     r10, #KEYV
        BL      CallVector
        MOV     r12, #IOC
 |
        BL      GotKey
 ]

        B       IrqRxAckScan

; *****************************************************************************

IrqTx   ROUT
        [ AssemblingArthur
        Push    "R4-R10, R14"           ; stack regs if new MOS IRQ vector
        |
        Push    "R4-R10"
        MOV     R12, #IOC               ; already set up in new IRQ scheme
        ]
        MOV     R11, #KeyWorkSpace

; First see if we're in a reset sequence

        LDRB    R0, ResetState          ; are we in a reset ?
        TEQ     R0, #0
        BEQ     %FT05                   ; not in a reset

        CMP     R0, #K1rak2             ; are we sending the reset nack ?
        BCS     %FT25                   ; no, just send reset code
        MOV     R1, #0                  ; yes, zero the reset state
        STRB    R1, ResetState
        STRB    R0, KARTTx
        Pull    $irqregs                ; don't disable TX

; Now see if any outstanding requests

05
        LDRB    R0, RequestSPD          ; is there an SPD request ?
        TEQ     R0, #0
        BEQ     %FT10                   ; [no SPD request]

        MOV     R1, #K1prst             ; code to send keyboard
        MOV     R2, #0                  ; no further SPD request
        STRB    R2, RequestSPD
        MOV     R2, #8
        STRB    R2, SPDRec              ; nibbles still to be sent/received
        STRB    R1, KARTTx              ; send the byte
        Pull    $irqregs                ; exit without disabling Tx

10
        LDRB    R0, RequestKbId         ; is there a pending keyboard request ?
        TEQ     R0, #0
        MOVNE   R1, #0
        STRNEB  R1, RequestKbId         ; no further request
        STRNEB  R0, KARTTx              ; send the byte
        Pull    $irqregs, NE            ; exit without disabling Tx

        LDRB    R0, RequestMouse        ; is there a pending mouse request ?
        TEQ     R0, #0
        MOVNE   R1, #0
        STRNEB  R1, RequestMouse        ; no further request
        STRNEB  R0, KARTTx
        Pull    $irqregs, NE            ; exit without disabling Tx

        LDRB    R0, RequestLED          ; is there a pending LED request ?
        TEQ     R0, #&FF
        MOVNE   R1, #&FF
        STRNEB  R1, RequestLED
        STRNEB  R0, KARTTx
        Pull    $irqregs, NE            ; exit without disabling Tx

        LDRB    R0, SPDRec              ; are we converting some SPD data
        TEQ     R0, #0
        BEQ     %FT20                   ; branch if not

        LDR     R1, SPDinput
        AND     R2, R1, #&F             ; get nybble to be sent
        ORR     R2, R2, #K1rqpd
        MOV     R1, R1, LSR #4          ; shift out the nybble sent
        STR     R1, SPDinput
        STRB    R2, KARTTx
        B       %FT30                   ; disable Tx, so we don't send another
                                        ; nibble before we get the conversion
20
        LDRB    R0, Reply
        TEQ     R0, #&FF
        BEQ     %FT30                   ; no reply to send
25
        STRB    R0, KARTTx              ; send the reply
        MOV     R0, #&FF
        STRB    R0, Reply               ; nothing else to send

        LDRB    R0, JustGotKbId
        TEQ     R0, #0                  ; if just got keyboard id then
        TXoff   R0, NE                  ;   disable all interrupts
        Pull    $irqregs, NE            ;   and wait for KEYV enable call
30
        RXonTXoff R0
        Pull    $irqregs

        END