The PSION Organiser II Homepage
 
Home
Technical Reference
Introduction
System
Versions
Operating
System
Memory Usage
Filing System
System Timing
System Board
Power Supply Board
Display
Keyboard
Interface Slots
Packs
General
Flashpacks
Low Level
Access
External
Interfacing
Comms Link
Psion Link
Protocol
Utility System
Services
Built-in
Applications
LZ Passwords
Programming
Language
General
Q-Code
Table
Interpreter
System Services
 

Technical Reference Manual

KEYBOARD

GENERAL

KEYBOARD VARIANTS

OPERATING THE KEYBOARD

KEYBOARD SCANNING

KEYBOARD INTERRUPTS

KEYBOARD INTERRUPTS (continued)

KEYBOARD VECTORS AND VARIABLES

SYSTEM SERVICES

EXAMPLE

GENERAL

The 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 VARIANTS

Variants 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.

standard keyboard
standard keyboard
Alpha-POS keyboard
Alpha-POS keyboard
numeic keyboard
numeric keyboard

OPERATING THE KEYBOARD

The 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 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:

  KEY VALUE
  ON/CLEAR 1
  MODE 2
  UP 3
  DOWN 4
  LEFT 5
  RIGHT 6
  SHIFT DEL 7
  DEL 8
  EXE 13

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.

  • Shift EXE
    To allow the use of SHIFT-EXE, bit 0 of KBB_SPEC must be set. When set, SHIFT-EXE will be returned as value 14.
  • Shift Space
    To allow the use of SHIFT-SPACE, bit 1 of KBB_SPEC must be set. When set, SHIFT-SPACE will be returned as value 15.
  • Shift Right-arrow
    SHIFT-RIGHT-ARROW is used to select foreign characters for the next key press only. For example, when the language is set to French, SHIFT-RIGHT arrow followed by 'a' produces the character 'á'.

KEYBOARD SCANNING

The ON/CLEAR key is polled independently of the others. The remaining 35 keys are polled on a 5 by 7 matrix.

THE ON/CLEAR KEY

Testing 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 MATRIX

The 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):

 

input
line
port 5 bits
KBD5 KBD4 KBD3 KBD2 KBD1
6 5 4 3 2
K7 D J P V S
K6 F L R X EXE
K5 G K Q W DEL
K4 C I O U Z
K3 B H N Y Y
K2 A G M S SHIFT
K1 right
arrow
left
arrow
down
arrow
up
arrow
MODE

 

To control the COUNTER lines, there are two significant addresses:

  1. SCA_COUNTERRESET (address $300) - sets all lines to zero.
  2. SCA_COUNTERCLOCK (address $340) - increments COUNTER address by one.

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 SCANNING

The 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:

  1. Check if ON/CLEAR key is pressed and exit if it is.
  2. Check quickly if any other key is pressed and exit if not.
  3. Determine which key is pressed and check if the SHIFT key is also pressed.

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:

K7 K6 K5 K4 K3 K2 K1 COUNTER VALUE
0 1 1 1 1 1 1 $3F
1 0 1 1 1 1 1 $5F
1 1 0 1 1 1 1 $6F
1 1 1 0 1 1 1 $77
1 1 1 1 0 1 1 $7B
1 1 1 1 1 0 1 $7D
1 1 1 1 1 1 0 $7E

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:

KEYBOARD STATE TIME TAKEN TO SCAN KEYBOARD
no key pressed 0.3ms
on/clear key pressed 0.2ms
any other key pressed 1.9ms

Note that the time taken for the entire keyboard interrupt may be increased by one or more of the following:

  1. The variable key click, 1ms by default
  2. The 'buffer-full' beep which lasts 10ms.
  3. Alarm checking and/or ringing which may last up to 2 mins (see Alarm Checking Interrupts).

KEYBOARD INTERRUPTS

The 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:

  1. Polling the keyboard using a routine at BTA_POLL
  2. Translating any key found using a routine at BTA_TRAN
  3. Producing a key 'click'
  4. Checking for alarms
  5. Incrementing a frame-counter
  6. Decrementing a display-timer

INITIALISING

Keyboard 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.

POLLING

The 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

TRANSLATING

The 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 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 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 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:
        ASCII  /zvpjd/
        .BYTE   K_EXE
        ASCII  /xrlf/
        ASCII  / wqke/
        ASCII  /yuoic/
        .BYTE   K_DEL
        ASCII  /tnhb/
        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
        ;==================

        ASCII  /.258)/
        .BYTE   K_EXE
                   $
        ASCII  $+-$
        ASCII  $/$
        ASCII  / 369%/
        ASCII  /0147(/
        .BYTE   K_DEL
        ASCII  /:$">/
        ASCII  /?;,=

BUFFERING

When a key press is detected, the 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 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 ASCII KEY IN BUFFER
        INC     KBB_NKYS                ;INCREMENT NUMBER OF KEYS
NOPUT:  CLI                             ;RESTORE INTERRUPTS

KEY CLICK

The 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 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 CHECKING

Alarms 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.

FRAME-COUNTER

A 16 bit frame-counter, TMW_FRAM (address $20CB,$20CC), is incremented by one by the keyboard interrupt and wraps around every 64k:

        LDX     TMW_FRAM
        INX
        STX     TMW_FRAM

It can be used as a random number or for timing. The rate that the counter runs at is the keyboard interrupt rate and so can be calculated as follows:

TIME BETWEEN INCREMENTS (IN SECS) = ( KBW_TDEL + 35 ) / 921600

KBW_TDEL is $B3DD by default, giving:

TIME = ( $B3DD + 35 ) / 921600 = 0.05 SECS

KBW_TDEL can be changed to adjust the timing but will also affect everything else controlled by the keyboard interrupts, e.g. key repeat rate.

Note that the rate of the frame-counter is not 100% accurate because interrupts are disabled while running some parts of the operating system.

DISPLAY TIMING

The rate of horizontal and vertical scrolling on the display is timed using DPW_REDY (address $006D,$006E). This variable is decremented by one by each keyboard interrupt until it reaches zero, i.e.:

        LDX     DPW_REDY:
        BEQ     NODEX
        DEX
        STX     DPW_REDY:
NODEX:

TESTING FOR KEYS

Keys are tested for by looking in the keyboard buffer. While testing for a key, if no key is found, the packs may be switched off, a low battery test is made, and the machine itself may switch off.

KEYTEST AND KEYGET

The system service KB$TEST will return the first key found in the keyboard buffer (zero if no key) but will not remove it. KB$GETK uses the same routine as KB$TEST but will wait for a key to be pressed and then remove it from the buffer.

If the function of key testing is to be changed, both KB$TEST and KB$GETK must be re-vectored by intercepting system calls through SWI, (KB$GETK does not call KB$TEST through the SWI). For example, KB$TEST may be rewritten to carry out a different task when low battery is detected.

PACK SWITCH OFF

If KB$TEST finds no keys in the keyboard buffer, the pack-switch-off flag KBB_PKOF is tested. If it is non-zero, the packs are switched off by calling the system service PK$PKOF (see chapter 9).

LOW BATTERY TEST

If KB$TEST finds no keys in the keyboard buffer, a low battery test is made. Bit 0 of PORT 5 is set when the battery supply voltage drops below approximately 5.2v. If this is the case, the message BATTERY TOO LOW is displayed and the machine will switch off after 4 seconds. When the machine is revived, the program will continue in KB$TEST where it left off.

MACHINE SWITCH OFF

If KB$TEST finds no keys in the keyboard buffer, the auto-switch-off flag, TMB_SWOF, is tested. If it is non-zero, the time remaining in seconds before switch off occurs, TMW_TOUT, is tested. If TMW_TOUT is zero, the machine is switched off by calling system service BT$SWOF. When the machine is revived, the program will continue in KB$TEST where it left off.

KEYBOARD VECTORS AND VARIABLES

The following vectors and variables allow the function of the keyboard to be customised very easily. They can all be read and written to directly, except for KBB_STAT, which should only be written to using system service KB$STAT.

KBB_STAT

KBB_STAT stores the following flags:

FLAG BIT OF KBB_STAT DESCRIPTION
KY_SHFT 7 SET IF SHIFT KEY PRESSED
KY_NUMB 6 SET IF NUMERIC LOCK
KY_CPNM 1 SET IF CAP OR NUM KEY
KY_CAPS 0 SET IF LOWER CASE LOCK

KBB_STAT can be read directly, but system service KB$STAT should be used to write to it.

BTA_POLL

This vector points to the routine which polls the keyboard. It must return the number of the key pressed in the A register and can set flags to be used by BTA_TRAN (e.g. shift, cap, num flags in KBB_STAT).

BTA_TRAN

This vector points to the routine which translates the key number supplied from BTA_POLL in the A register into the ASCII character it represents, returned in the A register. The routine will use flags which have been set by BTA_POLL, to decide how to translate the character. For example, if the SHIFT flag is set, a 'shifted' set of characters will be returned, or if a CAP flag is set, the characters will be be returned as lower case. To decode the character, a table of characters pointed to by BTA_TABL is used.

BTA_TABL

This is the vector which points to a table of characters used to translate a key press into an ASCII character. For an example, see above. The vector can be changed to point to a new set of characters and should contain 72 characters (36 'shifted') unless, of course, the translate routine has been changed or SHIFT has been disabled.

KBB_SHFK

This flag is used to disable the SHIFT function. It is tested only in the keyboard poll routine at BTA_POLL. If the flag is set, the SHIFT function is disabled and the SHIFT key will act as a normal key. Hence, it will return the 26th character of the lookup table which is currently a "?" character.

KBB_CAPK

This byte contains the number of the key (1 to 36) required to be the CAP key. It is used only by the keyboard translate routine at BTA_TRAN. By default it is set to 32 (the up-arrow key). To disable the CAP key altogether, a number greater than 36 should be stored in KBB_CAPK.

KBB_NUMK

This byte contains the number of the key required to be the NUM key. It works in exactly the same way as KBB_CAPK.

KBW_TDEL

This word controls the rate of keyboard interrupts. When an interrupt occurs the value in KBW_TDEL is stored in the TIMER 1 OUTPUT COMPARE REGISTER 1, and the FREE RUNNING COUNTER is set to zero, so that the next interrupt will occur after KBW_TDEL clock cycles.

Hence, the time between interrupts = ( KBW_TDEL + 35 ) / 921600 secs

There is an overhead of 35 cycles for each interrupt. The default value for KBW_TDEL is $B3DD, giving a time interval of 0.05 secs. Note that a value of zero in KBW_TDEL, will cause the machine to lock up.

KBB_DLAY

This byte stores the delay before auto-repeat of the keys begins in terms of the number of keyboard interrupts. The default value is 14, so with interrupts running at 20 times per second, the delay is 0.7 secs.

KBB_REPT

This byte stores the delay between keys when auto-repeating in terms of keyboard interrupts. The default value is 0 which is the fastest value. A value of 1 will repeat at half normal speed, 2 at a third normal speed etc.

KBB_CLIK

This byte stores the length of the keyboard click in ms. A value of zero will turn off the key click altogether. The default value is 1 giving the shortest possible click.

KBB_PKOF

This flag controls whether the packs are switched off by KB$TEST. If it is non-zero, which it is by default, the packs will be switched off whenever KB$TEST is called and there is are no keys in the keyboard buffer.

KBB_SPEC (LZ only)

Contains flags to handle the 'special' keys.

Bit 7 - set to disable all special SHIFT keys.
Bit 1 - set to allow SHIFT-SPACE.
Bit 0 - set to allow SHIFT-EXE.

SYSTEM SERVICES

This section describes the operating system calls available for keyboard handling.

KB$INIT

Initialises keyboard interrupts. The following tasks are carried out by this routine:

  1. The keyboard buffer is flushed of any characters.
  2. The keyboard interrupt rate KBW_TDEL is set to $B3DD.
  3. The initial delay and key repeat rate KBB_DLAY, KBB_REPT are set to 14 and 0 respectively.
  4. The keyboard status flags, in KBB_STAT are set to 0, i.e. KY_SHFT, KY_CPNM, KY_CAPS and KY_NUMB are cleared.
  5. The length of the key click, KBB_CLIK, is set to 1.
  6. The interrupts are enabled: FREE RUNNING COUNTER is set to 0, TIMER 1 OUTPUT COMPARE REGISTER 1 is set to $B3DD, bit 3 of TIMER 1 CONTROL STATUS REGISTER 1 is set, to enable the interrupt, and the I mask bit of the condition code register is cleared.

KB$TEST

Looks in keyboard buffer for a key but does not remove it. If the buffer is not empty, the ASCII value of the first key found in the buffer is returned in the B register. The unget buffer KBB_WAIT is tested for a key before looking in KBT_BUFF. If KBB_WAIT is empty and there is a key in KBT_BUFF, it is transferred to KBB_WAIT.

If no keys are found, the following will occur:

  1. If KBB_PKOF is non-zero, the pack will switch off using system service PK$PKOF.
  2. If TMB_SWOF is non-zero and TMW_TOUT is zero, the machine will switch off using system service BT$SWOF.
  3. If low battery is detected, BATTERY TOO LOW will be displayed for 4 secs before the machine switches off.

If the I mask (bit 4) of the condition code register is set (i.e. interrupts are disabled) when KB$TEST is called, it will poll the keyboard itself allowing the operating system to run with interrupts disabled. Every function of the keyboard interrupt routine is carried out except alarm checking. Any keys found are put in the keyboard buffer, the frame-counter is incremented and the display timer is decremented etc. There is a 50ms fixed delay after polling the keyboard so that if KB$TEST is called in a loop, the keyboard is polled approximately every 50ms. After polling the keyboard itself, KB$TEST looks in the keyboard buffer as usual.

KB$GETK

Waits for a key press and returns it in the B register. This routine uses the same code as KB$TEST. If there are no keys in the keyboard buffer, a SLP instruction is executed before next testing for a key, in order to save power. When a key is detected, it is removed from the buffer simply by clearing KBB_WAIT.

The auto-switch-off timeout counter TMW_TOUT is reset to the value in TMW_TCNT at the start of this routine, so the machine will not switch off in this routine for TMW_TCNT seconds.

KB$BREK

Tests if ON/CLEAR key is pressed. The key is tested for directly by reading PORT 5 and then searched for in the keyboard buffer KBT_BUFF. If ON/CLEAR is detected, KB$BREK waits for it to be released, then flushes the keyboard buffer and returns the carry flag set. If no ON/CLEAR key is found, the carry flag is cleared.

KB$FLSH

Flushes keyboard of any characters in type-ahead buffer. KBB_BACK, KBB_NKYS, KBB_PREV and KBB_WAIT are all set to zero.

KB$UGET

Puts the key supplied in the B register into the keyboard 'unget' buffer, KBB_WAIT. The buffer can only hold 1 key. If the buffer is not already full, the key is not placed in the buffer.

KB$STAT

Sets the state of the keyboard. The B register is stored into keyboard status byte KBB_STAT.

Bit 0 of the B register set indicates lower case, bit 6 set indicates numeric lock. Note that some other bits of KBB_STAT are used as flags and should not be affected. Hence KBB_STAT should be read and bits 0 and 6 either cleared or set before calling KB$STAT, see example below.

The state of the cursor (block or line) is determined by the new keyboard status and stored in DPB_CUST. A line cursor is for 'shift' mode, otherwise it's a block cursor. The new cursor type is immediately refreshed on the display.

EXAMPLE:

        LDA     B,KBB_STAT:     ;GET CURRENT KEYBOARD STATUS
        ORA     B,#KY_NUMB      ;NUMERIC LOCK. LINE CURSOR.
        OS      KB$STAT         ;SET KEYBOARD STATUS

KB$CONK (LZ only)

Exactly the same as KB$GETK but turns the cursor ON before waiting for a key and OFF afterwards.

EXAMPLE

FULL ASCII SET FROM KEYBOARD

The example code enables the whole ASCII character set to be accessed from the keyboard. The LEFT-ARROW and RIGHT-ARROW keys become two more SHIFT keys. Holding down LEFT-ARROW and pressing the P key, for example will return ASCII character 16 which, if displayed on the Organiser, is translated as a bell character. For all the characters returned in each 'shift' mode, see the table at the end of the example.

This example is taken from the code used in the RS232 device in terminal mode, to enable the whole ASCII set to be typed from the keyboard.

There are two routines provided in the file: KB_NEW and KB_OLD. KB_NEW must be called to set up the new keyboard and KB_OLD must be called to restore the normal keyboard.

See Example.

 
 
first previous   next top