![]() |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Technical Reference ManualKEYBOARDGENERALThe keyboard consists of 36 keys which are polled on interrupt at a variable interval, initially set to 50ms. The keys auto-repeat at a variable rate and key presses are stored in a 16 character type-ahead buffer. The keyboard lookup table, the polling, the key translation and the shift status can all be altered through the use of the keyboard vectors and variables. The SHIFT key can be disabled and the keys for CAP and NUM can be changed. The keyboard interrupts also control alarm checking and display timing, and increment a frame counter. While testing for a key, the datapacks or the machine itself may switch off and a low battery test is made. KEYBOARD VARIANTSVariants on the standard 36-key keyboard were also produced for particular customer requirements. These are either all-numeric or a special alphanumeric layout called "Alpha-POS". The Alpha-POS layout "inverts" the Alpha and Numeric keys by re-labeling the template around the keys, and the keycaps.
OPERATING THE KEYBOARDThe normal keyboard consists of a 6 by 6 matrix of keys, as shown above. The letter keys normally produce upper-case letters and return the corresponding ')"; onMouseout="hideddrivetip()"> ASCII value. By holding down the SHIFT key and pressing one of the letters you can access the symbols and numbers marked above the keys. To select lower-case letters, hold down the SHIFT key and press the CAP key. Repeat this to return the keyboard to upper-case mode. Pressing the SHIFT and NUM keys, puts you permanently in 'shift' mode so that the function of the SHIFT key is now reversed. The functions of SHIFT NUM, SHIFT CAP and SHIFT DEL, however, are not affected (DEL deletes characters to the left and SHIFT DEL deletes characters from the right). The top row of keys and some on the bottom row are special. These keys return the following values:
The SHIFT key, the CAP key and the NUM key do not return values but immediately carry out their function. The keyboard on the LZ has been extended to allow SHIFT-EXE, SHIFT-SPACE and SHIFT-RIGHT-ARROW. These SHIFT-KEY functions can be disabled by setting bit 7 of KBB_SPEC.
KEYBOARD SCANNINGThe ON/CLEAR key is polled independently of the others. The remaining 35 keys are polled on a 5 by 7 matrix. THE ON/CLEAR KEYTesting for the ON/CLEAR key is done by reading bit 7 of port 5 (address $15). If the key is pressed, bit 7 will be set, otherwise bit 7 is clear. Hence, the ON/CLEAR key can be tested directly and very quickly. The following routine waits for it to be pressed: TESTKEY:
LDA A,POB_PORT5: ;READ ADDRESS $15
BPL TESTKEY ;BRANCH IF BIT 7 IS CLEAR
In some applications it is necessary to protect against 'key bounce' and it is recommended that a delay of approximately 50ms is used. The following routine waits for the ON/CLEAR key to be released and then pauses for 50ms: DEBOUNCE:
LDA A,POB_PORT5: ;READ ADDRESS $15
BMI DEBOUNCE ;BRANCH IF BIT 7 IS SET
LDX #11600 ;FOR DELAY OF 50MS
1$: DEX
BNE 1$
Note that if keyboard interrupts are enabled while running the above procedure, holding down the ON/CLEAR key will fill up the keyboard buffer. A system service, KB$BREK, is provided to test if the ON/CLEAR key is pressed or if an ON/CLEAR key is in the keyboard buffer. THE KEY MATRIXThe key matrix consists of 7 columns, controlled by the SEMI-CUSTOM-CHIP 'COUNTER', and 5 rows which can be read as bits 2 to 6 of PORT 5 (the ON/CLEAR key uses bit 7). The layout is as follows (see also System Board - Keyboard):
To control the COUNTER lines, there are two significant addresses:
To carry out these functions, simply read or write to the respective address. The following will set the contents of the counter to $3F, i.e. K1 to K6 high and K7 low: TST SCA_COUNTERRESET ;SET COUNTER TO ZERO
LDA A,#$3F
1$: TST SCA_COUNTERCLOCK ;INCREMENT COUNTER
DEC A
BNE 1$
When a key is pressed, a connection is made from one of K1 to K7 to one of the PORT 5 lines. The lines on PORT 5 are pulled high, so by setting one of K1 to K7 low, a specific key press can be detected. For example, if K7 only is low, the 'D' key can be detected by bit 6 of PORT 5 going low. Polling the entire keyboard involves setting each of K1 to K7 low in turn, reading PORT 5, and decoding the key. By setting all 7 lines low simultaneously, a quick check for any key can be made. The following routine will test for any key press: TST SCA_COUNTERRESET ;SET K1 TO K7 LOW
LDA A,POB_PORT5: ;READ PORT 5
BMI CLRKEY ;BRANCH IF ON/CLEAR KEY IS PRESSED
AND A,#$7C ;IGNORE BITS 7,1 AND 0
CMP A,#$7C ;CHECK IF ANY BIT IS LOW
BNE AKEY ;BRANCH IF KEY PRESS DETECTED
The method of keyboard scanning is explained in the next section. KEY SCANNINGThe keyboard is scanned as fast as possible using the routine pointed to by the ram vector BTA_POLL. For maximum efficiency, the following method is used:
Steps 1 and 2 are described above. Step 3 involves setting each of the SEMI-CUSTOM-CHIP COUNTER lines (K1 to K7) low in turn. This is done most efficiently by setting K7 low first then K6 down to K1:
The following code will set the values $3F,$5F... etc. on the COUNTER: TST SCA_COUNTERRESET ;READ ADDRESS $300 TO ZERO COUNTER
LDA B,#$40
PSH B
DEC B ;INITIAL VALUE $3F
BRA CLOCKB
NEXTCOL:
PSH B
CLOCKB:
TST SCA_COUNTERCLOCK ;READ ADDRESS $340 TO INC COUNTER
DEC B ;DO IT B TIMES
BNE CLOCKB
BSR READPORT5 ;READ PORT 5 HERE AND DECODE KEY
PUL B
LSR B
BNE NEXTCOL ;REPEAT FOR COLUMNS K7 to K1
The SHIFT key is in column K2 so scanning of the keyboard must continue, even after one key has been found, to check if SHIFT is also pressed. This method results in the following approximate keyboard scan times:
Note that the time taken for the entire keyboard interrupt may be increased by one or more of the following:
KEYBOARD INTERRUPTSThe keyboard scanning routine is called at regular intervals from an interrupt generated by the processor's TIMER 1 counter. This is a 16 bit FREE RUNNING COUNTER (FRC) (address $09,$0A) incremented by the processor clock. The interrupt is generated when the value in the FRC matches a value set up in the TIMER 1 OUTPUT COMPARE REGISTER 1 (address $0B,$0C) and is directed to the address contained in the ROM at address $FFF4. The ram vector BTA_OCI is then used to jump to the interrupt service routine, i.e.: LDX BTA_OCI
JMP 0,X
The code in the interrupt service routine handles the following:
INITIALISINGKeyboard interrupts are fully initialised on a cold start of the machine only, using the system service KB$INIT. When each interrupt occurs, the first task is to set up the FREE RUNNING COUNTER and OUTPUT COMPARE REGISTER for the next interrupt, i.e.: LDA A,POB_TCSR1: ;MUST BE READ SO THAT A SUBSEQUENT WRITE TO
CLR A ; - OCR1 WILL CLEAR THE OUTPUT COMPARE FLAG
CLR B ; - SEE ACCOMPANYING 6301 BOOK
STD POW_FRC: ;RESET FREE RUNNING COUNTER TO ZERO
LDD KBW_TDEL: ;GET VALUE FOR KEYBOARD INTERRUPT RATE
STD POW_OCR1: ;SET OUTPUT COMPARE REGISTER
When the machine switches off, the value in TIMER CONTROL STATUS REGISTER 1 and the state of the INTERRUPT MASK are saved so that on a warm start they can be restored. Also, the keyboard buffer is flushed on a warm start using system service KB$FLSH. POLLINGThe routine to poll the keyboard is called through the ram vector BTA_POLL. The function of this routine is to scan the keyboard and return the key pressed in the A register and to set any flags required by the translating routine at ram vector BTA_TRAN. The following code can be used to poll the keyboard and will return the key pressed in the A register as a value between 0 and 36 (0 means no key): KB_POLL:
TST SCA_COUNTERRESET ;SET COUNTER TO ZERO
AIM #<$FF-KY_SHFT>,KBB_STAT: ;NO SHIFT KEY YET
LDA B,POB_PORT5: ;TEST FOR ON/CLEAR KEY
BPL NOTCLR ;BRANCH IF NOT PRESSED
LDA A,#36 ;36 REPRESENTS THE ON/CLEAR KEY
RTS
NOTCLR:
CLR A ;NO KEY YET
STA A,KBB_KNUM: ;KEY NUMBER BECOMES 0
PSH A ;PSH 0
BSR LOOKKEY ;ANY KEY IS PRESSED? SETS Z IF NOT
PUL A ;DOES NOT AFFECT Z FLAG
BEQ ENDKYBD ;BRANCH IF NO KEY FOUND IN LOOKKEY
LDA B,#$40
PSH B
DEC B
BRA CLOCKB
NEXTCOL:
PSH B
CLOCKB:
PSH A
1$: TST SCA_COUNTERCLOCK ;READ ADDRESS $340 TO INC COUNTER
DEC B ;DO IT B TIMES
BNE 1$
BSR LOOKKEY ;SETS B TO ROW NUMBER,IF KEY FOUND
PUL A ;IN THIS COLUMN
BEQ NOPSH ;BRANCH IF NO KEY IN THIS COLUMN
ADD B,KBB_KNUM: ;GOT A KEY BUT MUST CONTINUE,
TBA ;TO CHECK IF SHIFT IS ALSO PRESSED
NOPSH: LDA B,KBB_KNUM:
ADD B,#5
STA B,KBB_KNUM: ;POINT TO NEXT COLUMN
PUL B
LSR B ;SELECT NEXT COLUMN
BNE NEXTCOL
ENDKYBD:
RTS
LOOKKEY:
;SETS B TO ROW NUMBER (1-5) OF KEY IN CURRENT COLUMN
;B WILL BE ZERO IF NO KEY IN THIS COLUMN (OR JUST SHIFT)
;SETS KY_SHFT FLAG IF SHIFT IS PRESSED
;SETS Z ON B
;PRESERVES X
LDA B,#5 ;CHECK 5 ROWS
LDA A,POB_PORT5: ;READ PORT 5
ASL A ;IGNORE BIT 7
NEXTROW:ASL A ;ROTATE KEY INTO CARRY,CLR IF KEY PRESSED
BCC GOTKEY ;BRANCH IF KEY PRESS DETECTED
DEC B
BNE NEXTROW ;CHECK NEXT ROW
RTS ;RETURN WITH B=0 IF NO KEY FOUND
GOTKEY: ;B IS POSITION IN ROW
TST KBB_SHFK ;TEST SHIFT ENABLE FLAG
BNE 11$ ;EXIT IF SHIFT DISABLED, B IS NOT ZERO
PSH A
LDA A,KBB_KNUM: ;GET CURRENT KEY NUMBER
CMP A,#25 ;IS IT 6TH COLUMN, CONTAINING SHIFT KEY?
PUL A
BNE 11$ ;EXIT IF NOT, B IS NOT ZERO
DEC B ;COMPARE B WITH ROW 1 (ROW WITH SHIFT KEY)
BEQ 10$ ;BRANCH IF THIS IS THE SHIFT KEY (B IS 0)
PSH B ;ELSE CHECK IF SHIFT ALSO PRESSED
1$: ASL A
DEC B
BNE 1$
PUL B
INC B ;RESTORE B TO REAL KEY PRESSED
BCS 11$ ;BRANCH IF C SET (FROM ASL A)
10$: OIM #KY_SHFT,KBB_STAT: ;SET SHIFT-PRESSED FLAG
TST B ;SET Z FLAG ON B
11$: RTS
The auto-repeat of the keys is accomplished using KBB_PREV, KBB_DLAY, KBB_REPT and KBB_CNTR in the following manner:
LDX BTA_POLL ;POLL KEYBOARD (SEE ABOVE)
JSR 0,X ;RETURN KEY PRESS IN A
CMP A,KBB_PREV: ;IS SAME KEY STILL PRESSED ?
BEQ 1$ ;BRANCH IF YES
STA A,KBB_PREV: ;SAVE NEW KEY
LDA B,KBB_DLAY: ;DELAY BEFORE AUTO-REPEATING BEGINS
BRA 2$
1$: LDA B,KBB_REPT: ;DELAY BETWEEN KEYS DURING REPEAT
TST KBB_CNTR ;TEST DELAY COUNTER
BEQ 2$ ;BRANCH IF TIME TO RETURN KEY
LDA B,KBB_CNTR:
DEC B ;TO DECREMENT DELAY COUNTER
CLR A ;NO KEY UNTIL COUNTER IS ZERO
2$: STA B,KBB_CNTR: ;SET DELAY COUNTER
LDX BTA_TRAN
JSR 0,X ;TRANSLATE KEY IN A REGISTER
TIM #KY_CPNM,KBB_STAT: ;IS IT CAP OR NUM.
BNE DOCLICK ;IF SO, JUST EMIT KEY CLICK
TST A ;TEST IF KEY PRESSED
BEQ END ;END OF INTERRUPT IF NOT
;INSERT KEY INTO BUFFER - SEE BELOW
DOCLICK:
;EMIT KEY CLICK - SEE BELOW
END: RTI ;RETURN FROM INTERRUPT
TRANSLATINGThe ram vector BTA_TRAN points to the routine used to translate the key returned from BTA_POLL. The function of this routine is to translate the key number passed in the A register into an ')"; onMouseout="hideddrivetip()"> ASCII character and return it in the A register. The following code can be used to translate a key number (0 to 36) into the ')"; onMouseout="hideddrivetip()"> ASCII key it represents on the standard Organiser. It uses KBB_STAT to decide whether 'shifted' characters, or lower-case characters are returned and also to refresh the cursor in the correct state (block or line). If the key pressed is SHIFT CAP or SHIFT NUM, no key is returned (i.e. the A register is 0) but the KY_CPNM flag is set. KB_TRAN:
LDA B,KBB_STAT: ;GET KEYBOARD STATUS FLAGS
AND B,#<$FF-KY_CPNM> ;CLEAR CAP/NUM FLAG
BPL 3$ ;BRANCH IF NOT SHIFT,NOT CAP OR NUM
CMP A,KBB_CAPK ;IS IT THE CAP KEY ?
BNE 1$ ;BRANCH IF NOT
EOR B,#KY_CAPS ;TOGGLE CAPS FLAG
BRA 2$
1$: CMP A,KBB_NUMK ;IS IT THE NUM KEY ?
BNE 3$ ;BRANCH IF NOT
EOR B,#KY_NUMB ;TOGGLE NUM FLAG
2$: ORA B,#KY_CPNM ;SET CAP/NUM FLAG
CLR A ;RETURN NO KEY
3$: PSH A ;SAVE KEY PRESSED
OS KB$STAT ;SET KEYBOARD STATE
;PRESERVES B
TBA ;SET A TO KBB_STAT
PUL B ;B IS KEY PRESSED
TST B
BEQ 9$ ;BRANCH IF NO KEY PRESSED
PSH B
LDX BTA_TABL ;ADDRESS OF KEYBOARD LOOKUP TABLE
DEX ;SO KEY 1 IS FIRST KEY IN TABLE
LDA B,KBB_STAT: ;GET KEYBOARD STATUS
ASL B ;EXCLUSIVE OR KY_SHFT WITH KY_NUMB
BVC 4$ ;BRANCH IF NOT 'SHIFT' MODE
LDA B, 36
ABX ;SKIP TO 'SHIFTED' LOOKUP TABLE
4$: PUL B
ABX ;INDEX INTO TABLE
LDA B,0,X ;GET
')";
onMouseout="hideddrivetip()">
ASCII KEY FROM TABLE
LSR A ;TEST CAPS FLAG
BCS 5$ ;IF SET LEAVE AS LOWER CASE
CMP B,#^a/a/
BCS 5$
CMP B,#^a/z/
BHI 5$
ADD B,#^a/A/-^a/a/ ;CONVERT B TO UPPER CASE
5$:
CMP B,#K_DEL ;IS IT DELETE KEY
BNE 9$ ;BRANCH IF NOT
ASL A ;TEST SHFT FLAG
BPL 9$ ;BRANCH IF NOT SHIFT
DEC B ;SHIFT DEL IS ALWAYS DEL RIGHT
9$: TBA ;RETURN TRANSLATED KEY IN A
RTS
The keyboard table pointer KBA_TABL points to the following table by
default:
KBT_TABL:
')";
onMouseout="hideddrivetip()">
ASCII /zvpjd/
.BYTE K_EXE
')";
onMouseout="hideddrivetip()">
ASCII /xrlf/
')";
onMouseout="hideddrivetip()">
ASCII / wqke/
')";
onMouseout="hideddrivetip()">
ASCII /yuoic/
.BYTE K_DEL
')";
onMouseout="hideddrivetip()">
ASCII /tnhb/
')";
onMouseout="hideddrivetip()">
ASCII /?smga/ ;(?=SHIFT - NOT RETURNED EVER !)
.BYTE K_MODE
.BYTE K_UP,K_DOWN
.BYTE K_LEFT,K_RGHT
.BYTE K_AC ;ON/CLEAR KEY
;SHIFTED CHARACTERS
;==================
')";
onMouseout="hideddrivetip()">
ASCII /.258)/
.BYTE K_EXE
$
')";
onMouseout="hideddrivetip()">
ASCII $+-$
')";
onMouseout="hideddrivetip()">
ASCII $/$
')";
onMouseout="hideddrivetip()">
ASCII / 369%/
')";
onMouseout="hideddrivetip()">
ASCII /0147(/
.BYTE K_DEL
')";
onMouseout="hideddrivetip()">
ASCII /:$">/
')";
onMouseout="hideddrivetip()">
ASCII /?;,=
BUFFERINGWhen a key press is detected, the ')"; onMouseout="hideddrivetip()"> ASCII character for that key is placed in a 16 byte wrap-around buffer, KBT_BUFF. An offset into the buffer KBB_BACK and a count of the number of characters in the buffer KBB_NKYS are used to implement the buffering. If the buffer is already full, the character is not stored, but a 'buffer-full' beep is emitted lasting 10ms. The following code can be used to buffer the character in the A register:
LDA B,KBB_NKYS: ;GET NUMBER OF KEYS IN BUFFER
LDX #10 ;10MS FOR 'BUFFER-FULL' BEEP
CMP B,#16 ;IS BUFFER FULL ?
BEQ DOBUZ ;BRANCH IF YES
LDX #KBT_BUFF ;ADDRESS OF KEYBOARD BUFFER
ADD B,KBB_BACK: ;OFFSET INTO BUFFER
AND B,#$0F ;WRAP AROUND
ABX
STA A,0,X ;STORE
')";
onMouseout="hideddrivetip()">
ASCII KEY IN BUFFER
INC KBB_NKYS ;INCREMENT NUMBER OF KEYS
DOCLICK:
LDA B,KBB_CLIK ;LENGTH OF KEY CLICK
BEQ END ;NO CLICK IF ZERO
DEC B ;LENGTH 1 WILL GIVE < 1MS CLICK
CLR A
XGDX ;DURATION IN X
DOBUZ: LDD TMW_TCNT ;SIZE OF SWITCH OFF TIME OUT
STD TMW_TOUT: ;RESET TIMEOUT COUNT IF KEY PRESSED
LDD #56 ;A GOOD NOTE
OS BZ$TONE ;BEEP FOR X MS
END:
The keyboard buffer can be cleared out using system service KB$FLSH and there is a facility for inserting a key into a 1 byte buffer KBB_WAIT by using system service KB$UGET. KBB_WAIT is always tested for a key before looking in the main keyboard buffer KBT_BUFF. Keys can also be inserted into KBT_BUFF, but keyboard interrupts must be prevented while this is done. The following code will insert the key contained in the A register: PUTA:
SEI ;STOP INTERRUPTS OCCURRING IN HERE
LDA B,KBB_NKYS: ;GET NUMBER OF KEYS IN BUFFER
CMP B,#16 ;IS BUFFER FULL ?
BEQ NOPUT ;BRANCH IF YES
LDX #KBT_BUFF ;ADDRESS OF KEYBOARD BUFFER
ADD B,KBB_BACK: ;OFFSET INTO BUFFER
AND B,#$0F ;WRAP AROUND
ABX
STA A,0,X ;STORE
')";
onMouseout="hideddrivetip()">
ASCII KEY IN BUFFER
INC KBB_NKYS ;INCREMENT NUMBER OF KEYS
NOPUT: CLI ;RESTORE INTERRUPTS
KEY CLICKThe key click is produced in the keyboard interrupt by using the routine for system service BZ$TONE, using the following code: KEYCLICK:
LDA B,KBB_CLIK ;LENGTH OF KEY CLICK
BEQ END ;NO CLICK IF ZERO
DEC B ;LENGTH 1 WILL GIVE < 1MS CLICK
CLR A
XGDX ;DURATION IN X
LDD #56 ;A GOOD NOTE
JSR BZ_TONE ;BEEP FOR X MS
END:
Note: BZ_TONE is JSR'ed to and not called as an operating system service. Hence, intercepting operating system calls to BZ$TONE (by re-vectoring SWI) will not affect the key click. JSR'ing or JMP'ing to operating system services can only be done within the operating system and not by external routines. The length of the key click, KBB_CLIK, can be changed. Zero will disable the click and any value up to $FF will specify the length of the click in milliseconds. Warning: BZ_TONE toggles the alarm line to produce the click (using addresses SCA_ALARMHIGH and SCA_ALARMLOW) but if SOE_B is high, it is possible that 21v will appear on any devices present, see low level pack access. For example if an ')"; onMouseout="hideddrivetip()"> EPROM device is selected, a byte may be blown on the pack. Since the click occurs on interrupt, care must be taken to ensure interrupts are disabled whenever SOE_B is set high. ALARM CHECKINGAlarms and diary alarms are checked for only when the flag AMB_DOIT is set (e.g. by the NMI on a minute boundary): TST AMB_DOIT ;TEST DOIT FLAG
BEQ 1$ ;BRANCH IF CLEAR
;ALARM CHECKING CODE GOES HERE
CLR AMB_DOIT ;CLEAR DOIT FLAG
1$:
The method of alarm checking is described in chapter 19. AMB_DOIT is cleared when alarm checking is finished to prevent it being done on the next interrupt. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||