; File: CC.PROM.KB
; Date: 15-Nov-83
; By:   Keith Ball
;
; KEYBOARD DRIVER FOR PROM (kb)

;
; EQUATES FOR ALL KEYBOARD SOFTWARE
;
; KEYBOARD DATA AREA DEFINITIONS
;
KBBflgs EQU     0               ;FLAG JUST HI ORDER BYTE
KBBfrnt EQU     KBBflgs+2       ;FRONT PTR SAVE
KBBrear EQU     KBBfrnt+4       ;REAR PTR SAVE
KBBsrsv EQU     KBBrear+4       ;STATUS REG SAVE AREA
KBBbufr EQU     KBBsrsv+2       ;KEYBOARD BUFFER
KBBlen  EQU     RAMkblen-KBBbufr;NMBR OF BYTES IN BUFFER
;
; FLAG BIT DEFINITIONS
;
KBFfull EQU     0               ;BUFFER FULL FLAG
KBFemty EQU     1               ;BUFFER EMPTY FLAG
KBFclos EQU     2               ;KEY CLOSURE FLAG
KBFshft EQU     3               ;SHIFT KEY
KBFcntl EQU     4               ;CONTROL KEY
KBFlock EQU     5               ;SHIFT LOCK KEY
;
; MISCELLANEOUS EQUATES
;
KBmsk40 EQU     $1F             ;MASK TO CLEAR D7-D5 (CONTROL CODE)
;
; TABLE VALUES FOR PROCESSING CHARACTERS
;
KBCqual EQU     $7F             ;QUALIFIER VALUES > THEN THIS
KBCshft EQU     $FE             ;TABLE VALUE FOR SHIFT
KBCcntl EQU     $FD             ;TABLE VALUE FOR CONTROL
KBClock EQU     $FC             ;TABLE VALUE FOR SHIFT LOCK
KBCnoch EQU     $FF             ;TABLE VALUE FOR NO CHAR CDE
;
; SPECIAL ASCII CHARACTERS
;
KBClca  EQU     'a'             ;LOWER CASE A
KBClcz  EQU     'z'             ;LOWER CASE Z
KBCqmrk EQU     '?'             ;QUESTION MARK

        page
;
; COMMAND AND CONTROL REGISTER VALUES
;
KBccOff EQU     $02             ;TURN OFF UART (CMD)
KBcc600 EQU     $17             ;600 BAUD AND 8 BIT XMIT (CTL)
KBccBrk EQU     $08             ;XMIT A BREAK (CMD)
KBccGo  EQU     $09             ;TURN ON INTS & UART (CMD)
;
KBdsInt EQU     $0700           ;DISABLE 68000 INTERRUPTS

;
; KBinit - Initialize (reset) keyboard
;
;      REGISTER A2 IS USED AS POINTER TO COMMAND REGISTER
;      REGISTER A3 IS ADDRESS OF KBRD DATA AREA
;
KBinit  MOVEM.L D0/A0-A3,-(SP)          ;save registers
        LEA     RAMkbbuf.W,A3
        LEA     KBRcmnd.L,A2
        MOVE.B  #KBccOff,(A2)           ;TURN OFF KBRD
        LEA     KBBflgs(A3),A0          ;CLEAR INT HANDLER FLAGS
        CLR.L   (A0)                    ;INCLUDES QUALIFIERS
        BSET    #KBFemty,(A0)           ;BUFFER IS EMPTY
        ;
        ; INITIALIZE FRONT & REAR POINTERS
        ;
        LEA     KBBbufr(A3),A0
        LEA     KBBfrnt(A3),A1
        MOVE.L  A0,(A1)+
        MOVE.L  A0,(A1)
        LEA     KBintr,A0               ;SETUP AUTOVECTOR 6
        MOVE.L  A0,IVlvl6.W             ;WITH ADDR OF INT HANDLER
        ;
        ; TURN ON KEYBOARD UART
        ;
        MOVE.B  KBRstat-KBRcmnd(A2),D0  ;RESET UART                  0.8
        MOVE.B  KBRdata-KBRcmnd(A2),D0  ;CLEAR RECEIVE               0.8
        MOVE.B  #KBcc600,KBRcntl-KBRcmnd(A2) ;8 BITS, 600 BAUD XMIT  0.8
        MOVE.B  #KBccBrk,(A2)           ;FORCE BREAK OF KBRD
        MOVE.W  #33333,D0               ;DELAY FOR UART TO DO BREAK
KBinit1 DBF     D0,KBinit1              ;NEED MINIMUM OF 33.3 MILLISECS
        MOVE.B  #KBccGo,(A2)            ;TURN ON UART & INTERRUPTS
        move.w  #$2500,sr               ;set priority to 6, KYBD intr only
        MOVEM.L (SP)+,D0/A0-A3          ;restore registers
        RTS

        page
;
; KBintr - Keyboard interrupt service routine
;
; BEGIN INTERRUPT SERVICE ROUTINE.  THIS IS THE ENTRY POINT.  IT'S ADDRESS
; MUST BE PLACED IN AUTO VECTOR INTERRUPT 6 VECTOR BEFORE KEYBOARD INTERRUPT
; IS TURNED ON.
;
; REGISTER USEAGE:  D0 - KEYCODE
;                   D1 - CHARACTER
;                   A0 - ADDRESS OF FLAG BYTE
;                   A2 - BASE ADDRESS OF KBRD DATA AREA
;
KBintr  MOVEM.L D0-A6,-(SP)             ;SAVE REGISTERS ON STACK
        LEA     RAMkbbuf.W,A2           ;BASE ADDR OF KBRD DATA AREA
        BSR     KBgetky                 ;GET KEYCODE FROM UART DATA PORT
        ;
        ; IF BIT 7 OF KEYCODE SET THEN CLOSURE ELSE RELEASE
        ;
        LEA     KBBflgs(A2),A0
        BCLR    #KBFclos,(A0)           ;ASSUME RELEASE
        BTST    #7,D0                   ;KEYCODE BIT D7 CLEAR?
        BEQ.S   KBintr1                 ;YES
        BSET    #KBFclos,(A0)
        BCLR    #7,D0
        ;
        ; GET CHARACTER CODE FOR THIS KEYCODE
        ;
KBintr1 LEA     KBstable,A1             ;ASSUME SHIFT TABLE
        BTST    #KBFshft,(A0)
        BNE.S   KBintr2                 ;SHIFT BIT SET
        LEA     KBrtable,A1             ;ELSE USE REGULAR TABLE
KBintr2 MOVE.B  0(A1,D0.W),D1           ;INDEX TABLE BY KEYCODE
        ;
        ; IF CHAR(D1) = $FF THEN IGNORE AND EXIT
        ;
        CMPI.B  #KBCnoch,D1
        BEQ.S   KBintr9
        BSR.S   KBproky                 ;ELSE PROCESS KEYCODE
        ;
        ; EXIT INTERRUPT SERVICE ROUTINE
        ;
KBintr9 MOVEM.L (SP)+,D0-A6             ;RESTORE REGISTERS
        RTE                             ;EXIT INTERRUPT

        page
;
; KBgetch - Get a keyboard character
;
; Register useage:  A0 = Front pointer
;                   A1 = address of end of buffer + 1
;                   A2 = updated front pointer
;                   A3 = address of front pointer
;                   A4 = address of flag byte
;                   A5 = address of keyboard data area
;                   A6 = address of Status Register save area
;
;       Exit:   D0.B - Next character in buffer
;
KBgetch MOVEM.L A0-A6,-(SP)             ;save all address registers
        LEA     RAMkbbuf.W,A5           ;keyboard data area
        LEA     KBBflgs(A5),A4          ;address of Flag byte
        tst.b   CPextcrt.w              ;using external CRT?            0.8
        bon.s   KBgchra                 ;yes, use data comm 0           0.8
        bsr     SBprom2                 ;debug boot PROM?               0.7
        bne.s   KBgchr1                 ;no, go on                      0.7
KBgchra move.l  #KBcPort,a1             ;get UART pointer               0.8
KBgchr0 btst    #KBrdBit,KBuSt(a1)      ;is char in UART?               0.7
        boff.s  KBgchr0                 ;no, wait some more             0.7
        move.b  KBuDa(a1),d0            ;get character                  0.7
        bra.s   KBgchr9                 ;return                         0.7
        ;
        ; Wait for a character in the Buffer.
        ;
KBgchr1 BTST    #KBFemty,(A4)           ;while (Buffer_empty) do;
        BNE.S   KBgchr1                 ;*
        ;
        ; have char, check for wrap around before get char
        ;
        LEA     KBBfrnt(A5),A3          ;pointer to Front save loc
        MOVE.L  (A3),A0                 ;Front pointer
        LEA     KBBbufr+KBBlen(A5),A1   ;end of buffer + 1
        MOVE.L  A0,A2                   ;
        ADDQ.L  #1,A2                   ;add one to pointer to get next addr
        CMPA.L  A1,A2                   ;Front=end of buffer + 1        0.7
        BNE.S   KBgchr2                 ;No
        LEA     KBBbufr(A5),A2          ;yes, then wrap back to beginning
                                        ;
KBgchr2
;-----  LEA     KBBsrsv(A5),A6          ;don't need                     0.8
        MOVE.W  SR,-(SP)                ;put SR on stack                0.8
        ORI.W   #KBdsInt,SR             ;*** disable interrupts
        MOVE.B  (A0),D0                 ;get char
        MOVE.L  A2,(A3)                 ;save new Front value
        CMPA.L  KBBrear(A5),A2          ;if Front=Rear then
        BNE.S   KBgchr3                 ;Buffer_empty := true;
        BSET    #KBFemty,(A4)           ;
                                        ;
KBgchr3 MOVE.W  (SP)+,SR                ;*** enable interrupts          0.8
KBgchr9 MOVEM.L (SP)+,A0-A6             ;restore callers address regs   0.7
        RTS                             ;return

        page
;
; KBgetky - GET KEYCODE (IGNORES ERRORS)
;
;     EXIT : (D0) - UART DATA PORT BYTE
;
KBgetky CLR.L   D0                      ;MAKE SURE HI 3 BYTES ARE 0
        ;
        ; READ STATUS REGISTER TO CLEAR IRQ BIT
        ; ALWAYS READ DATA PORT SO IF OVERRUN THEN FOR NEXT CHAR
        ; IT WILL BE CLEARED.
        ;
        MOVE.B  KBRstat.L,D1            ;GET STATUS OF RECEIVE
        MOVE.B  KBRdata.L,D0            ;READ UART DATA PORT
        RTS

        page
;
; KBproky - PROCESS CHARACTER OR QUALIFIER
;
;       Enter:  D1 = CHARACTER CODE FROM TABLE
;               D0 = KEYCODE
;               A0 = ADDRESS OF FLAGS
;
KBproky CMPI.B  #KBCqual,D1             ;IS IT A QUALIFIER
        BHI.S   KBpro3                  ;YES
        ;
        ; IGNORE REST OF KEYS IF NOT CLOSURE
        ;
        BTST    #KBFclos,(A0)
        BEQ.S   KBpro9
        ;
        ; TEST FOR CONTROL
        ;
        BTST    #KBFcntl,(A0)
        BEQ.S   KBpro1                  ;NO,TRY SHIFT LOCK
        CMPI.B  #KBCqmrk,D1
        BLS.S   KBpro1
        ANDI.B  #KBmsk40,D1             ;CLEAR BITS D7,D6,D5 OF CHAR
        BRA.S   KBpro2                  ;PUT CHAR
        ;
        ; TEST FOR SHIFT LOCK
        ;
KBpro1  BTST    #KBFlock,(A0)
        BEQ.S   KBpro2                  ;KEY NOT DOWN
        CMPI.B  #KBClca,D1
        BCS.S   KBpro2                  ;NOT WITHIN RANGE
        CMPI.B  #KBClcz,D1
        BHI.S   KBpro2                  ;NOT WITHIN RANGE
        LEA     KBstable,A1
        MOVE.B  0(A1,D0.W),D1           ;INDEX TABLE BY KEYCODE
        ;
        ; IF BUFFER NOT FULL PUT CHARACTER
        ;
KBpro2  BTST    #KBFfull,(A0)
        BNE.S   KBpro9
        BSR.S   KBputch
        BRA.S   KBpro9
        ;
        ; PROCESS A QUALIFIER KEY
        ;
KBpro3  BSR.S   KBqual
KBpro9  RTS

        page
;
; KBputch - PUT ONE CHARACTER IN BUFFER
;
;       Enter:  D1 = BYTE TO PUT IN BUFFER
;               A0 = ADDRESS OF FLAGS
;               A2 = ADDRESS OF KEYBOARD DATA AREA
;
        ;
        ; PUT CHARACTER IN CIRCULAR QUEUE AT REAR
        ;
KBputch LEA     KBBrear(A2),A5          ;get pointer to rear pointer
        MOVE.L  (A5),A3                 ;*
        MOVE.B  D1,(A3)+                ;UPDATE POINTER ALSO
        ;
        ; IF REAR > ENDBUFFER THEN REAR := @BUFFER
        ;
        LEA     KBBbufr+KBBlen(A2),A4   ; A4 = end buffer + 1
        CMPA.L  A4,A3                   ;
        BNE.S   KBput1                  ;NOT BEYOND BUFFER
        LEA     KBBbufr(A2),A3          ;
        ;
        ; IF FRONT = REAR THEN BUFFER FULL
        ;
KBput1  CMPA.L  KBBfrnt(A2),A3          ;
        BNE.S   KBput2                  ;
        BSET    #KBFfull,(A0)           ;
KBput2  MOVE.L  A3,(A5)                 ;UPDATE REAR IN MEMORY
        BCLR    #KBFemty,(A0)           ;SHOW BUFFER NOT EMPTY
        RTS                             ;return

        page
;
; KBqual - PROCESS QUALIFIER KEYS
;
;       Enter:  D1 = CHARACTER CODE FROM TABLE
;               A0 = ADDRESS OF FLAGS
;
KBqual  CMPI.B  #KBCshft,D1             ;IS IT SHIFT?
        BNE.S   KBqual1                 ;NO
        MOVEQ   #KBFshft,D2             ;BIT POSITION OF SHIFT
        BRA.S   KBqual3                 ;CHANGE FLAG
                                        ;
KBqual1 CMPI.B  #KBCcntl,D1             ;IS IT CONTROL?
        BNE.S   KBqual2                 ;NO
        MOVEQ   #KBFcntl,D2             ;BIT POSITION OF CONTROL
        BRA.S   KBqual3                 ;CHANGE FLAG
                                        ;
KBqual2 CMPI.B  #KBClock,D1             ;IS IT SHIFT LOCK?
        BNE.S   KBqual9                 ;NO,THEN IT'S GARBAGE
        MOVEQ   #KBFlock,D2
        ;
        ; IF CLOSURE THEN SET FLAG ELSE CLEAR FLAG
        ;
KBqual3 BTST    #KBFclos,(A0)
        BEQ.S   KBqual8
        BSET    D2,(A0)
        BRA.S   KBqual9
KBqual8 BCLR    D2,(A0)
KBqual9 RTS

        page
;
; THE SHIFT TABLE
;        TABLE IS INDEXED BY KEYCODE.  EACH BYTE REPRESENTS THE ENTRY FOR
;        THE CORRESPONDING KEYCODE.
;
;           0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
KBstable
  ;        ..   3   9  ..   6   ,   -  cr  ..   1   7  ..   4   8   5   2
  DATA.B  $FF,$33,$39,$FF,$36,$2C,$2D,$0D,$FF,$31,$37,$FF,$34,$38,$35,$32
; ;         +  ..   {   |  cr   }  bs  ..   )   ?   P   _   :   ~   "  ..
; DATA.B  $2B,$FF,$7B,$7C,$0D,$7D,$08,$FF,$29,$3F,$50,$5F,$3A,$7E,$22,$FE
  ;         +  ..   {  bs  cr   }   |  ..   )   ?   P   _   :   ~   "  .. ;0.6
  DATA.B  $2B,$FF,$7B,$08,$0D,$7D,$7C,$FF,$29,$3F,$50,$5F,$3A,$7E,$22,$FE ;0.6
  ;        ..  ..  ..  ..  ..  ..  ..  ..   $   %   R   T   F   G   V   B
  DATA.B  $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$24,$25,$52,$54,$46,$47,$56,$42
  ;         @   #   W   E   S   D   X   C esc   !  ..   Q  ..   A  ..   Z
  DATA.B  $40,$23,$57,$45,$53,$44,$58,$43,$1B,$21,$FF,$51,$FC,$41,$FE,$5A
  ;         ^   &   Y   U   H   J   N   M  ..  ..  ..  sp  ..   0  ..   .
  DATA.B  $5E,$26,$59,$55,$48,$4A,$4E,$4D,$FD,$FF,$FF,$20,$FF,$30,$FF,$2E
  ;         *   (   I   O   K   L   <   >  ..  ..  ..  ..  ..  ..  ..  ..
  DATA.B  $2A,$28,$49,$4F,$4B,$4C,$3C,$3E,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF

        page
;
; THE REGULAR TABLE - UNSHIFTED OR LOWER CASE
;        TABLE IS INDEXED BY KEYCODE.  EACH BYTE REPRESENTS THE ENTRY FOR
;        THE CORRESPONDING KEYCODE.
;
;           0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
KBrtable
  ;        ..   3   9  ..   6   ,   -  cr  ..   1   7  ..   4   8   5   2
  DATA.B  $FF,$33,$39,$FF,$36,$2C,$2D,$0D,$FF,$31,$37,$FF,$34,$38,$35,$32
; ;         =  ..   [   \  cr   ]  bs  ..   0   /   p   -   ;   `   ;  ..
; DATA.B  $3D,$FF,$5B,$5C,$0D,$5D,$08,$FF,$30,$2F,$70,$2D,$3B,$60,$27,$FE
  ;         =  ..   [  bs  cr   ]   \  ..   0   /   p   -   ;   `   ;  .. ;0.6
  DATA.B  $3D,$FF,$5B,$08,$0D,$5D,$5C,$FF,$30,$2F,$70,$2D,$3B,$60,$27,$FE ;0.6
  ;        ..  ..  ..  ..  ..  ..  ..  ..   4   5   r   t   f   g   v   b
  DATA.B  $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$34,$35,$72,$74,$66,$67,$76,$62
  ;         2   3   w   e   s   d   x   c esc   1  ..   q  ..   a  ..   z
  DATA.B  $32,$33,$77,$65,$73,$64,$78,$63,$1B,$31,$FF,$71,$FC,$61,$FE,$7A
  ;         6   7   y   u   h   j   n   m  ..  ..  ..  sp  ..   0  ..   .
  DATA.B  $36,$37,$79,$75,$68,$6A,$6E,$6D,$FD,$FF,$FF,$20,$FF,$30,$FF,$2E
  ;         8   9   i   o   k   l   ,   .  ..  ..  ..  ..  ..  ..  ..  ..
  DATA.B  $38,$39,$69,$6F,$6B,$6C,$2C,$2E,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
