home *** CD-ROM | disk | FTP | other *** search
- three-key rollover FOR THE c-128 AND c-64.
-
- BY cRAIG bRUCE <CSBRUCE@NEUMANN.UWATERLOO.CA>
-
- 1. introduction
-
- tHIS ARTICLE EXAMINES A THREE-KEY ROLLOVER MECHANISM FOR THE KEYBOARDS OF THE
- c-128 AND c-64 AND PRESENTS kERNAL-WEDGE IMPLEMENTATIONS FOR BOTH MACHINES.
- wEBSTER'S DOESN'T SEEM TO KNOW, SO i'LL TELL YOU THAT THIS MEANS THAT THE
- MACHINE WILL ACT SENSIBLY IF YOU ARE HOLDING DOWN ONE KEY AND THEN PRESS
- ANOTHER WITHOUT RELEASING THE FIRST (OR EVEN PRESS A THIRD KEY WHILE HOLDING
- DOWN TWO OTHERS). tHIS IS USEFUL TO FAST TOUCH TYPERS. iN FACT, FAST TYPING
- WITHOUT ROLLOVER CAN BE QUITE ANNOYING; YOU GET A LOT OF MISSING LETTERS.
-
- aNOTHER ANNOYING PROPERTY OF THE KERNEL KEYSCANNING IS JOYSTICK INTERFERENCE.
- iF YOU MOVE THE JOYSTICK PLUGGED INTO PORT #1, YOU WILL NOTICE THAT SOME JUNK
- KEYSTROKES RESULT. tHE KEYSCANNERS HERE ELIMINATE THIS PROBLEM BY SIMPLY
- CHECKING IF THE JOYSTICK IS PRESSED AND IGNORING THE KEYBOARD IF IT IS.
-
- tHE REASON THAT A 3-KEY ROLLOVER IS IMPLEMENTED INSTEAD OF THE MORE GENERAL
- n-KEY ROLLOVER IS THAT SCANNING THE KEYBOARD BECOMES MORE AND MORE UNRELIABLE
- AS MORE KEYS ARE HELD DOWN. kEY "SHADDOWS" BEGIN TO APPEAR TO MAKE IT LOOK
- LIKE YOU ARE HOLDING DOWN A CERTAIN KEY WHEN YOU REALLY ARE NOT. sO, BY
- LIMITING THE NUMBER OF KEYS SCANNED TO 3, SOME OF THIS CAN BE AVOIDED. yOU
- WILL GET STRANGE RESULTS IF YOU HOLD DOWN MORE THAN THREE KEYS AT A TIME, AND
- EVEN SOMETIMES WHEN HOLDING DOWN 3 OR LESS. tHE "SHIFT" KEYS (sHIFT,
- cOMMODORE, cONTROL, aLTERNATE, AND cAPSlOCK) DON'T COUNT IN THE 3 KEYS OF
- ROLLOVER, BUT THEY DO MAKE THE KEYBOARD HARDER TO READ CORRECTLY.
- fORTUNATELY, THREE KEYS WILL ALLOW YOU TO TYPE WORDS LIKE "and" AND "the"
- WITHOUT RELEASING ANY KEYS.
-
- 2. user guide
-
- uSING THESE UTILITIES IS REALLY EASY - YOU JUST TYPE AWAY LIKE NORMAL. tO
- INSTALL THE c-128 VERSION, ENTER:
-
- boot "keyscan128"
-
- AND YOU'RE IN BUSINESS. tHE PROGRAM WILL DISPLAY "kEYSCAN128 INSTALLED" AND
- GO TO WORK. tHE PROGRAM LOADS INTO MEMORY AT ADDRESSES $1500-$17ba (5376-6074
- DECIMAL), SO YOU'LL WANT TO WATCH OUT FOR CONFLICTS WITH OTHER UTILITIES.
- tHIS PROGRAM ALSO TAKES OVER THE irq VECTOR AND THE basic RESTART VECTOR
- ($a00). tHE PROGRAM WILL SURVIVE A run/stop+restore. tO UNINSTALL THIS
- PROGRAM, YOU MUST RESET THE MACHINE (OR POKE THE KERNEL VALUES BACK INTO THE
- VECTORS); IT DOES NOT UNINSTALL ITSELF.
-
- lOADING THE c-64 VERSION IS A BIT TRICKIER, SO A SMALL basic LOADER PROGRAM IS
- PROVIDED. load AND run THE "keyscan64.boot" PROGRAM. iT WILL LOAD THE
- "keyscan64" PROGRAM INTO MEMORY AT ADDRESSES $c500-$c77e (50432-51070 DECIMAL)
- AND EXECUTE IT (WITH A sys 50432). tO UNINSTALL THE PROGRAM, ENTER sys 50435.
- tHE PROGRAM TAKES OVER THE irq AND nmi VECTORS AND ONLY GIVES THEM BACK TO THE
- KERNEL UPON UNINSTALLATION. tHE PROGRAM WILL SURVIVE A run/stop+restore.
-
- sOMETHING THAT YOU MAY OR MAY NOT KNOW ABOUT THE c-64 IS THAT ITS KEYS CAN BE
- MADE TO REPEAT BY POKING TO ADDRESS 650 DECIMAL. poke650,128 WILL ENABLE THE
- REPEATING OF ALL KEYS. poke650,0 WILL ENABLE ONLY THE REPEATING OF THE space,
- delete, AND cursor KEYS. poke650,64 WILL DISABLE THE REPEATING OF ALL KEYS.
- aN UNUSUAL SIDE EFFECT OF CHANGING THIS TO EITHER FULL REPEAT OR NO REPEAT IS
- THAT HOLDING DOWN shift+commodore (CHARACTER SET SHIFT) WILL REPEAT RAPIDLY.
-
- tO SEE THE ROLLOVER IN ACTION, HOLD DOWN THE "j" KEY FOR A WHILE, AND THEN
- PRESS "k" WITHOUT RELEASING "j". "k" WILL COME OUT AS EXPECTED, AS IT WOULD
- WITH THE KERNAL. nOW, RELEASE THE "j" KEY. iF YOU ARE ON A c-128, YOU WILL
- NOTICE THAT THE "k" KEY WILL NOW STOP REPEATING (THIS IS ACTUALLY AN IMPORTANT
- FEATURE - IT AVOIDS PROBLEMS IF YOU DON'T RELEASE ALL OF THE KEYS YOU ARE
- HOLDING DOWN, AT ONCE). nOW, PRESS AND HOLD THE "j" KEY AGAIN WITHOUT
- RELEASING THE "k". "j" WILL NOW APPEAR. iT WOULDN'T USING THE kERNAL KEY
- SCANNER. yOU CAN ALSO TRY THIS WITH 3-KEY COMBINATIONS. tHERE WILL BE SOME
- COMBINATIONS THAT CAUSE PROBLEMS; MORE ON THIS BELOW.
-
- aLSO, TAKE A SPAZ ON THE JOYSTICK PLUGGED INTO PORT #1 AND OBSERVE THAT NO
- GARBAGE GETS TYPED IN. tHIS WAS AN ANNOYING PROBLEM WITH THE KERNEL OF BOTH
- THE 64 AND 128 AND HAS LEAD MANY DIFFERENT GAMES TO PICKING BETWEEN JOYSTICK
- #1 AND #2 AS THE PRIMARY CONTROLLER. tHE JOYSTICK IN PORT #2 IS NOT A PROBLEM
- TO EITHER kEYSCAN-128/64 OR THE kERNAL.
-
- 3. keyboard scanning
-
- tHE kERNAL SCANS THE KEYBOARD SIXTY TIMES A SECOND TO SEE WHAT KEYS YOU ARE
- HOLDING DOWN. bECAUSE OF HARDWARE PECULIARITIES, THERE ARE MULTIPLE SCANNING
- TECHNIQUES THAT WILL GIVE DIFFERENT RESULTS.
-
- 3.1. scanning example
-
- aN EXAMPLE PROGRAM IS INCLUDED TO DEMONSTRATE DIFFERENT KEYBOARD SCANNING
- TECHNIQUES POSSIBLE. tO RUN IT FROM A c-128 IN 40-COLUMN (SLOW) MODE, ENTER:
-
- boot "keyshow"
-
- oN A c-64, YOU MUST:
-
- load "keyshow",8,1
-
- AND THEN:
-
- sys 4864
-
- tHE SAME PROGRAM WORKS ON BOTH MACHINES. fOUR MAPS OF THE KEYSCANNING MATRIX
- WILL BE DISPLAYED ON THE 40-COLUMN SCREEN, AS SCANNED BY DIFFERENT TECHNIQUES.
- tHE LEFTMOST ONE IS SCANNED FROM TOP TO BOTTOM "QUICKLY". tHE SECOND FROM THE
- LEFT SCANS FROM BOTTOM TO TOP "QUICKLY". tHE THIRD FROM THE LEFT SCANS THE
- KEYBOARD SIDEWAYS, AND THE RIGHTMOST MATRIX SCANS THE KEYS FROM TOP TO BOTTOM
- "SLOWLY".
-
- tHE MAPPING OF KEYSCAN MATRIX POSITIONS TO KEYS IS AS FOLLOWS:
-
- rows: \ columns: PEEK($dc01)
- POKE \
- $dc00 \ 128 64 32 16 8 4 2 1
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 255-1 {$7c} down {$7c} f5 {$7c} f3 {$7c} f1 {$7c} f7 {$7c} right {$7c} return{$7c} delete{$7c}
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 255-2 {$7c}left-sh{$7c} e {$7c} s {$7c} z {$7c} 4 {$7c} a {$7c} w {$7c} 3 {$7c}
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 255-4 {$7c} x {$7c} t {$7c} f {$7c} c {$7c} 6 {$7c} d {$7c} r {$7c} 5 {$7c}
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 255-8 {$7c} v {$7c} u {$7c} h {$7c} b {$7c} 8 {$7c} g {$7c} y {$7c} 7 {$7c}
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 255-16 {$7c} n {$7c} o {$7c} k {$7c} m {$7c} 0 {$7c} j {$7c} i {$7c} 9 {$7c}
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 255-32 {$7c} , {$7c} @ {$7c} : {$7c} . {$7c} - {$7c} l {$7c} p {$7c} + {$7c}
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 255-64 {$7c} / {$7c} ^ {$7c} = {$7c}rght-sh{$7c} home {$7c} ; {$7c} * {$7c} \ {$7c}
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 255-128 {$7c} stop {$7c} q {$7c}commodr{$7c} space {$7c} 2 {$7c}control{$7c} _ {$7c} 1 {$7c}
- +-------+-------+-------+-------+-------+-------+-------+-------+
-
- tHE FOLLOWING TABLE CONTAINS THE ADDITIONAL KEYS WHICH MUST BE SCANNED ON THE
- c128 (BUT WHICH ARE NOT DISPLAYED BY THE EXAMPLE SCANNING PROGRAM).
-
- rows: \ columns: PEEK($dc01)
- POKE \
- $d02f \ 128 64 32 16 8 4 2 1
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 255-1 {$7c} 1 {$7c} 7 {$7c} 4 {$7c} 2 {$7c} tab {$7c} 5 {$7c} 8 {$7c} help {$7c}
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 255-2 {$7c} 3 {$7c} 9 {$7c} 6 {$7c} enter {$7c} lf {$7c} - {$7c} + {$7c} esc {$7c}
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 255-4 {$7c}no-scrl{$7c} right {$7c} left {$7c} down {$7c} up {$7c} . {$7c} 0 {$7c} alt {$7c}
- +-------+-------+-------+-------+-------+-------+-------+-------+
-
- tHESE TABLES ARE PRESENTED ON PAGE 642 OF THE cOMMODORE 128 pROGRAMMER'S
- rEFERENCE gUIDE. tHE SCAN CODES THAT ARE STORED IN LOCATION 212 ON THE c128
- AND LOCATION 197 ON THE c64 ARE CALCULATED BASED ON THE ABOVE TABLES. tHE
- ENTRY IN THE "1" BIT POSITION OF THE FIRST LINE OF THE FIRST TABLE (delete)
- HAS A SCAN CODE OF 0, THE "2" ENTRY (return) HAS A SCAN CODE OF 1, ETC., THE
- ENTRY ON THE SECOND SCAN LINE IN THE "1" POSITION ("3") HAS A SCAN CODE OF 8,
- ETC., ALL THE WAY DOWN TO CODE 63. tHE SCAN CODES FOR THE 128 GO ALL THE WAY
- TO 87, CONTINUING IN THE SECOND TABLE LIKE THE FIRST.
-
- yOU WILL NOTICE SOME STRANGE EFFECTS OF THE DIFFERENT SCANNING TECHNIQUES WHEN
- YOU HOLD DOWN MULTIPLE KEYS. mORE ON THIS BELOW. aLSO TRY PUSHING JOYSTICK
- #1 AROUND.
-
- 3.2. scanning hardware
-
- tO SCAN THE 128 KEYBOARD, YOU MUST POKE A VALUE INTO $dc00 (cia#1 PORT a) AND
- $d02f (vic CHIP KEYBOARD SELECT PORT) TO SELECT THE ROW TO BE SCANNED. tHE
- dATA dIRECTION rEGISTER FOR THIS PORT WILL BE SET TO ALL OUTPUTS BY THE
- kERNAL, SO YOU DON'T HAVE TO WORRY ABOUT IT. eACH BIT OF $dc00 AND THE THREE
- LEAST SIGNIFICANT BITS OF $d02f ARE USED FOR SELECTING ROWS. a "0" BIT MEANS
- THAT A ROW is SELECTED, AND A "1" MEANS THAT A ROW is not SELECTED. tHE POKE
- VALUE TO USE FOR SELECTING AMONG THE VARIOUS ROWS ARE GIVEN IN THE TWO TABLES
- IN THE PREVIOUS SECTION.
-
- uSING ONE BIT PER ROW ALLOWS YOU TO SELECT MULTIPLE ROWS AT THE SAME TIME. iT
- CAN BE USEFUL TO SELECT ALL ROWS AT ONE TIME OR NO ROWS. tO READ THE ROW THAT
- HAS BEEN SELECTED, SIMPLY PEEK AT LOCATION $dc01 (cia#1 PORT b). eACH BIT
- WILL TELL YOU WHETHER THE CORRESPONDING KEY IS CURRENTLY BEING HELD DOWN OR
- NOT. aGAIN, WE HAVE REVERSE LOGIC; A "0" MEANS THAT THE KEY IS BEING HELD
- DOWN, AND A "1" MEANS THAT THE KEY IS NOT HELD DOWN. tHE BIT VALUES
- CORRESPONDING TO THE KEYS ARE GIVEN AS THE COLUMN HEADINGS IN THE TABLES IN
- THE PREVIOUS SECTION. sINCE THERE IS NO SUCH THING AS A PERFECT MECHANICAL
- SWITCH, IT IS RECOMMENDED THAT YOU "DEBOUNCE" EACH KEY ROW READ IN THE
- FOLLOWING WAY:
-
- AGAIN:
- LDA $DC01
- CMP $DC01
- BNE AGAIN
-
- sO, TO SCAN THE ENTIRE KEYBOARD, YOU SIMPLY SELECT EACH SCAN ROW IN SOME
- ORDER, AND READ AND REMEMBER THE KEYS HELD DOWN ON THE ROW. aS IT TURNS OUT,
- YOU HAVE TO BE A BIT CAREFUL OF EXACTLY HOW YOU "SELECT" A ROW. aLSO, THERE
- IS A SHORTCUT THAT YOU CAN TAKE. iN ORDER TO FIND OUT IF ANY KEY IS BEING
- HELD DOWN IN ONE OPERATION, ALL YOU HAVE TO DO IS SELECT ALL ROWS AT ONCE AND
- SEE IF THERE ARE ANY "0" BITS IN THE READ VALUE. iF SO, THERE IS A KEY BEING
- HELD DOWN SOMEWHERE; IF NOT, THEN THERE IS NO KEY BEING HELD DOWN, SO YOU
- DON'T HAVE TO BOTHER SCANNING THE ENTIRE KEYBOARD. tHIS WILL REDUCE OUR
- KEYSCANNING SIGNIFICANTLY, WHICH IS IMPORTANT, SINCE THE KEYBOARD WILL BE
- SCANNED EVERY 1/60 OF A SECOND.
-
- aS MENTIONED ABOVE, JOYSTICK #1 WILL INTERFERE WITH THE kERNAL READING THE
- KEYBOARD. tHIS IS BECAUSE THE READ VALUE OF JOYSTICK #1 IS WIRED INTO cia#1
- PORT a, THE SAME PLACE THAT THE KEYBOARD READ IS WIRED IN. sO, WHENEVER A
- SWITCH IN THE JOYSTICK IS PUSHED, THE CORRESPONDING BIT OF THE KEYBOARD SCAN
- REGISTER WILL BE FORCED TO "0", REGARDLESS OF WHICH KEYS ARE PRESSED AND
- REGARDLESS OF WHICH SCAN ROW IS SELECTED. tHERE'S THE CATCH. iF WE WERE TO
- UN-SELECT ALL SCAN ROWS AND STILL NOTICE "0"S IN THE KEYBOARD READ REGISTER,
- THEN WE WOULD KNOW THAT THE JOYSTICK WAS BEING PUSHED AND WOULD INTERFERE WITH
- OUR KEYBOARD SCANNING, SO WE COULD ABORT KEYBOARD SCANNING AND HANDLE THIS
- CASE AS IF NO KEYS WERE BEING HELD DOWN.
-
- iT STILL WOULD BE POSSIBLE BUT UNLIKELY THAT THE USER COULD PUSH THE JOYSTICK
- IN THE MIDDLE OF US SCANNING THE KEYBOARD AND SCREW UP OUR RESULTS, SO TO
- DEFEND AGAINST THIS, WE CHECK FOR THE JOYSTICK BEING PUSHED BOTH BEFORE AND
- AFTER SCANNING THE KEYBOARD. iF WE FIND THAT THE JOYSTICK IS PUSHED AT EITHER
- OF THESE TIMES, THEN WE THROW OUT THE RESULTS AND ASSUME THAT NO KEYS ARE HELD
- DOWN. tHIS WAY, THE ONLY WAY THAT A USER COULD SCREW UP THE SCANNING IS IF
- HE/SHE/IT WERE TO PRESS A SWITCH AFTER WE BEGIN SCANNING AND RELEASE IT BEFORE
- WE FINISH SCANNING. nOT BLOODY LIKELY FOR A HUMAN.
-
- yOU GET THE SAME DEAL FOR KEYBOARD SCANNING ON THE 64, EXCEPT YOU ONLY NEED TO
- USE $dc00 FOR SELECTING THE SCAN ROWS. aLSO NOTE THAT YOU WILL NOT BE ABLE TO
- PLAY WITH KEYBOARD SCANNING FROM basic BECAUSE OF THE INTERRUPT READING OF THE
- KEYBOARD. yOU MUST MAKE SURE THAT INTERRUPTS ARE DISABLED WHEN PLAYING WITH
- THE KEYBOARD HARDWARE, OR INTERRUPT SCANNING CAN COME ALONG AT ANY TIME AND
- CHANGE ALL OF THE REGISTER SETTINGS.
-
- 3.3. scanning source code
-
- tHE FOUR KEYBOARD SCANNING TECHNIQUES OF THE EXAMPLE PROGRAM ARE PRESENTED
- BELOW. tHE DECLARATIONS REQUIRED FOR ALL OF THEM ARE:
-
- PA = $DC00 ;ROW SELECT
- PB = $DC01 ;COLUMN READ
- DDRA = $DC02 ;DDR FOR ROW SELECT
- DDRB = $DC03 ;DDR FOR COLUMN READ
- SCANtABLE .BUF 8 ;STORAGE FOR SCAN
- MASK = $03 ;WORK LOCATION
-
- tHE CODE IS AS FOLLOWS, IN bUDDY FORMAT. eACH ROUTINE SCANS THE KEYBOARD AND
- STORES THE RESULTS IN THE "SCANtABLE" TABLE.
-
- ------------------+------------------+------------------+------------------+
- rOW FORWARD FAST {$7c} rOW BACKWARD FAST{$7c} cOLUMN RIGHT {$7c} rOW FORWARD SLOW
- ------------------+------------------+------------------+------------------+
- SEI {$7c} SEI {$7c} SEI {$7c} SEI
- LDX #0 {$7c} LDX #7 {$7c} LDA #$00 {$7c} LDX #0
- LDA #$FE {$7c} LDA #$7F {$7c} STA DDRA {$7c} LDA #$FE
- STA PA {$7c} STA PA {$7c} LDA #$FF {$7c} STA MASK
- NEXTrOW = * {$7c} NEXTrOW = * {$7c} STA DDRB {$7c} NEXTrOW = *
- - LDA PB {$7c}- LDA PB {$7c} LDY #7 {$7c} LDA MASK
- CMP PB {$7c} CMP PB {$7c} LDA #$7F {$7c} STA PA
- BNE - {$7c} BNE - {$7c} STA MASK {$7c}- LDA PB
- EOR #$FF {$7c} EOR #$FF {$7c} NEXTcOL = * {$7c} CMP PB
- STA SCANtABLE,X {$7c} STA SCANtABLE,X {$7c} LDA MASK {$7c} BNE -
- SEC {$7c} SEC {$7c} STA PB {$7c} EOR #$FF
- ROL PA {$7c} ROR PA {$7c}- LDA PA {$7c} STA SCANtABLE,X
- INX {$7c} DEX {$7c} CMP PA {$7c} SEC
- CPX #8 {$7c} BPL NEXTrOW {$7c} BNE - {$7c} ROL MASK
- BCC NEXTrOW {$7c} CLI {$7c} LDX #$FF {$7c} INX
- CLI {$7c} RTS {$7c} STX PB {$7c} CPX #8
- RTS {$7c} {$7c} EOR #$FF {$7c} BCC NEXTrOW
- ------------------+------------------+ LDX #7 {$7c} CLI
- {$7c}- ASL {$7c} RTS
- tHE FORWARD "QUICK" SCANNING STORES {$7c} ROL SCANtABLE,X +------------------+
- THE SCAN ROW SELECTION MASK INTO {$7c} DEX {$7c}
- THE ROW SELECTION REGISTER AND {$7c} BPL - {$7c}
- SHIFTS THE "0" BIT ONE POSITION {$7c} SEC {$7c}
- "FORWARD" FOR EACH ROW, DIRECTLY, {$7c} ROR MASK {$7c}
- USING A "ROL $DC00" INSTRUCTION. {$7c} DEY {$7c}
- tHIS WOULD PROBABLY BE THE OBVIOUS {$7c} BPL NEXTcOL {$7c}
- SOLUTION TO AN OPTIMIZING ASSEMBLER {$7c} LDA #$FF {$7c}
- PROGRAMMER. hOWEVER, FOR SOME {$7c} STA DDRA {$7c}
- REASON NOT QUITE UNDERSTOOD BY THIS {$7c} LDA #$00 {$7c}
- AUTHOR, THERE ARE "SHADOWING" {$7c} STA DDRB {$7c}
- PROBLEMS WITH THIS APPROACH. iF {$7c} CLI {$7c}
- YOU WERE TO HOLD DOWN THE TWO KEYS {$7c} RTS {$7c}
- "h" AND "k" AT THE SAME TIME, YOU +------------------+
- WOULD NOTICE THAT THESE TWO KEYS
- ARE ON THE SAME COLUMN OF TWO SUCCESSIVE ROWS. iF YOU HOLD THEM BOTH DOWN,
- YOU WILL SEE THE TWO POSITIONS BECOME ACTIVE, BUT SO WILL THE SAME COLUMN OF
- ALL SUCCESSIVE ROWS AFTER THE "h" AND "k", EVEN THOUGH THESE OTHER KEYS ARE
- NOT ACTUALLY HELD DOWN. yOU WILL GET AN INACCURATE READING IF BAD KEYS ARE
- HELD DOWN SIMULTANEOUSLY. yOU WILL NOTICE THE USE OF THE TERM "ACTIVE" ABOVE.
- tHIS IS BECAUSE ALTHOUGH THE HARDWARE RETURNS A "0" FOR ACTIVE, THE ROUTINE
- CONVERTS THAT INTO A "1" FOR EASIER PROCESSING LATER. i AM NOT SURE IF
- EVERYONE WILL GET THIS SAME RESULT, BUT IF YOUR KEYBOARD IS WIRED THE SAME AS
- MINE, YOU WILL.
-
- tHE BACKWARD "QUICK" SCANNING OPERATES QUITE SIMILARLY TO THE FORWARD
- SCANNING, EXCEPT FOR THE DIRECTION OF THE SCAN AND THE DIRECTION OF THE
- "SHADOW"; THE SHADOW GOES UPWARDS. yOU MIGHT THINK THAT andING TOGETHER THE
- RESULTS OF THE FORWARD AND BACKWARD SCAN TOGETHER WOULD ELIMINATE THE SHADOW,
- BUT THIS WILL NOT WORK SINCE ANY ROWS BETWEEN THE ROWS CONTAINING THE TWO KEYS
- HELD DOWN WILL BE INCORRECTLY READ AS BEING ACTIVE.
-
- tHE COLUMNWISE RIGHT SCANNING IS THE MOST COMPLICATED BECAUSE THE ROWS MUST BE
- CONVERTED INTO COLUMNS, TO ALLOW THE SCAN MATRIX TO BE INTERPRETED AS BEFORE.
- aLSO, THE dATA dIRECTION rEGISTERS HAVE TO BE CHANGED. yOU MIGHT THINK THAT
- COMBINGING ROW-WISE SCANNING WITH COLUMNWISE SCANNING WOULD GIVE BETTER
- RESULTS, AND IT PROBABLY WOULD, IF IT WEREN'T FOR A BIZARRE HARDWARE PROBLEM.
- iF YOU HOLD DOWN TWO OR MORE KEYS ON THE SAME SCAN ROW, SAY "w" AND "e", SOME
- OF THE KEYS WILL FLICKER OR DISAPPEAR, GIVING AN INACCURATE READING.
-
- tHE FORWARD "SLOW" SCANNING IS THE BEST OF THE BUNCH. iNCIDENTALLY, IT IS
- WHAT THE kERNAL USES (AS NEAR AS i CAN FIGURE - THEIR CODE IS EXTREMELY
- CONVOLUTED). tHIS TECHNIQUE IS THE SAME AS THE FORWARD "QUICK SCAN," EXCEPT
- THAT THE ROW SELECTION MASK IS SHIFTED IN A WORKING STORAGE LOCATION AND POKED
- INTO THE cia REGISTER, RATHER THAN BEING SHIFTED IN PLACE. i DON'T KNOW WHY
- THIS MAKES A DIFFERENCE, BUT IT DOES. tHERE IS STILL A PROBLEM WITH THIS
- TECHNIQUE, BUT THIS PROBLEM OCCURS WITH ALL TECHNIQUES. iF YOU HOLD DOWN
- THREE KEYS THAT FORM THREE "CORNERS" OF A RECTANGLE IN THE SCANNING MATRIX,
- THEN THE MISSING CORNER WILL BE READ AS BEING HELD DOWN ALSO. fOR EXAMPLE, IF
- YOU HOLD DOWN "c", "n", AND "m", THEN THE KEYBOARD HARDWARE WILL ALSO THINK
- THAT YOU ARE HOLDING DOWN THE "x" KEY. tHIS IS WHY THIS ARTICLE IMPLEMENTS A
- "THREE-KEY" ROLLOVER RATHER THAN AN "n-KEY" ROLLOVER. mANY THREE-KEY
- COMBINATIONS WILL STILL BE INTERPRETED CORRECTLY. nOTE, HOWEVER, THAT SHIFT
- KEYS SUCH AS shift OR control WILL ADD ONE MORE KEY TO THE HARDWARE SCANNING
- (BUT WILL NOT BE COUNTED IN THE THREE-KEY ROLLOVER), MAKING INACCURATE RESULTS
- MORE LIKELY IF YOU ARE HOLDING DOWN MULTIPLE OTHER KEYS AT THE SAME TIME.
-
- 4. the c-128 keyscanner
-
- tHIS SECTION GIVES THE SOURCE CODE FOR THE c-128 IMPLEMENTATION OF THE
- THREE-KEY ROLLOVER. tHE FORWARD "SLOW" KEY MATRIX SCANNING TECHNIQUE IS USED,
- EXTENDED TO WORK WITH THE EXTRA KEYS OF THE 128. iT WAS A BIT OF A PAIN
- WEDGING INTO THE kERNAL, SINCE THERE IS NOT A CONVENIENT INDIRECT jmp INTO
- SCANNING THE KEYBOARD, LIKE THERE ARE FOR DECODING AND BUFFERING PRESSED KEYS.
- a RATHER LENGTHY irq "PREAMBLE" HAD TO BE COPIED FROM THE rom, UP TO THE
- POINT WHERE IT jsrS TO THE KEYSCANNING ROUTINE. tHIS CODE IN INCLUDED IN
- THE FORM OF A ".BYTE" TABLE, TO SPARE YOU THE DETAILS.
-
- bEFORE SCANNING THE KEYBOARD, WE CHECK TO SEE IF JOYSTICK #1 IS PUSHED AND IF
- A KEY IS ACTUALLY PRESSED. iF NOT, WE ABORT SCANNING AND jmp TO THE KEY
- REPEAT HANDLING IN THE rom. iF A KEY IS HELD DOWN, WE SCAN THE KEYBOARD AND
- THEN EXAMINE THE RESULT. fIRST WE CHECK FOR THE SHIFT KEYS (shift, commodore,
- control, alt, AND caps lock), PUT THEM INTO LOCATION $d3 (SHIFT FLAGS) IN BIT
- POSTITIONS 1, 2, 4, 8, AND 16, RESPECTIVELY, AND REMOVE THEM FROM THE SCAN
- MATRIX. tHE caps lock KEY IS NOT ON THE MAIN KEY MATRIX; IT IS READ FROM THE
- PROCESSOR i/o PORT. tHIS IS GOOD, BECAUSE OTHERWISE WE COULD NOT ABORT
- SCANNING IF IT WERE THE ONLY KEY HELD DOWN.
-
- tHEN WE SCAN THE KEYMATRIX FOR THE FIRST THREE KEYS THAT ARE BEING HELD DOWN,
- OR AS MANY AS ARE HELD DOWN IF LESS THAN THREE. wE STORE THE SCAN CODES OF
- THESE KEYS INTO A 3-ELEMENT ARRAY. wE ALSO RETAIN A COPY OF THE 3-ELEMENT
- ARRAY FROM THE PREVIOUS SCAN AND WE CHECK FOR DIFFERENT KEYS BEING IN THE TWO
- ARRAYS. iF THE OLD ARRAY CONTAINS A KEY THAT IS NOT PRESENT IN THE NEW ARRAY,
- THEN THE USE HAS RELEASED A KEY, SO WE SET A FLAG TO INHIBIT INTERPRETATION OF
- KEYS AND PRETEND THAT NO KEYS ARE HELD DOWN. tHIS IS TO ELIMINATE UNDESIRABLE
- EFFECTS OF HAVING OTHER KEYS HELD DOWN REPEAT IF YOU RELEASE THE MOST RECENTLY
- PUSHED KEY FIRST. pc KEYBOARDS DO THIS. tHIS INHIBITING WILL BE IGNORED IF
- NEW KEYS ARE DISCOVERED IN THE NEXT STEP.
-
- iF THERE ARE KEYS IN THE NEW ARRAY THAT ARE NOT IN THE OLD, THEN THE USER HAS
- JUST PRESSED A NEW KEY, SO THAT NEW KEY GOES TO THE HEAD OF THE OLD ARRAY AND
- WE STOP COMPARING THE ARRAYS THERE. tHE KEY IN THE FIRST POSITION OF THE OLD
- ARRAY IS POKED INTO THE kERNAL "KEY HELD DOWN" LOCATION FOR THE kERNAL TO
- INTERPRET LATER. iF MORE THAN ONE NEW KEY IS DISCOVERED AT THE SAME TIME,
- THEN EACH OF THE NEW KEYS WILL BE PICKED UP ON SUCCESSIVE KEYBOARD SCANS AND
- WILL BE INTERPRETED AS JUST BEING PUSHED. sO, IF YOU PRESS THE "a", "n", AND
- "d" KEYS ALL AT THE SAME TIME, SOME PERMUTATION OF ALL THREE OF THESE KEYS
- WILL APPEAR ON THE SCREEN.
-
- wHEN WE ARE DONE INTERPRETING THE KEYS, WE CHECK THE JOYSTICK ONCE MORE AND IF
- IT IS STILL INACTIVE, WE PRESENT THE MOST RECENTLY PUSHED DOWN KEY TO THE
- kERNAL AND jmp INTO THE rom KEYBOARD DECODING ROUTINE.
-
- uNLIKE IN PREVIOUS ISSUES, THIS SOURCE CODE IS HERE IN LITERAL FORM; JUST
- EXTRACT EVERYTHING BETWEEN THE "-----=-----"S TO NAB THE SOURCE FOR YOURSELF.
- tHE SOURCE IS IN bUDDY ASSEMBLER FORMAT.
-
- -----=-----
- ;3-kEY rOLLOVER-128 BY cRAIG bRUCE 18-jUN-93 FOR c= hACKING MAGAZINE
-
- .ORG $1500
- .OBJ "@0:KEYSCAN128"
-
- SCANROWS = 11
- ROLLOVER = 3
-
- PA = $DC00
- PB = $DC01
- PK = $D02F
-
- JMP INITIALiNSTALL
-
- ;UGLY irq PATCH CODE.
-
- IRQ = * ;$1503
- .BYTE $D8,$20,$0A,$15,$4C,$69,$FA,$38,$AD,$19,$D0,$29,$01,$F0,$07,$8D
- .BYTE $19,$D0,$A5,$D8,$C9,$FF,$F0,$6F,$2C,$11,$D0,$30,$04,$29,$40,$D0
- .BYTE $31,$38,$A5,$D8,$F0,$2C,$24,$D8,$50,$06,$AD,$34,$0A,$8D,$12,$D0
- .BYTE $A5,$01,$29,$FD,$09,$04,$48,$AD,$2D,$0A,$48,$AD,$11,$D0,$29,$7F
- .BYTE $09,$20,$A8,$AD,$16,$D0,$24,$D8,$30,$03,$29,$EF,$2C,$09,$10,$AA
- .BYTE $D0,$28,$A9,$FF,$8D,$12,$D0,$A5,$01,$09,$02,$29,$FB,$05,$D9,$48
- .BYTE $AD,$2C,$0A,$48,$AD,$11,$D0,$29,$5F,$A8,$AD,$16,$D0,$29,$EF,$AA
- .BYTE $B0,$08,$A2,$07,$CA,$D0,$FD,$EA,$EA,$AA,$68,$8D,$18,$D0,$68,$85
- .BYTE $01,$8C,$11,$D0,$8E,$16,$D0,$B0,$13,$AD,$30,$D0,$29,$01,$F0,$0C
- .BYTE $A5,$D8,$29,$40,$F0,$06,$AD,$11,$D0,$10,$01,$38,$58,$90,$07,$20
- .BYTE $AA,$15,$20,$E7,$C6,$38,$60
-
- ;KEYSCANNING ENTRY POINT
-
- MAIN = *
- LDA #0 ;CHECK IF ANY KEYS ARE HELD DOWN
- STA PA
- STA PK
- - LDA PB
- CMP PB
- BNE -
- CMP #$FF
- BEQ NOkEYpRESSED ;IF NOT, THEN DON'T SCAN KEYBOARD, GOTO kERNAL
-
- JSR CHECKjOYSTICK ;IF SO, MAKE SURE JOYSTICK NOT PRESSED
- BCC JOYSTICKpRESSED
- JSR KEYSCAN ;SCAN THE KEYBOARD AND STORE RESULTS
- JSR CHECKjOYSTICK ;MAKE SURE JOYSTICK NOT PRESSED AGAIN
- BCC JOYSTICKpRESSED
- JSR SHIFTDECODE ;DECODE THE SHIFT KEYS
- JSR KEYDECODE ;DECODE THE FIRST 3 REGULAR KEYS HELD DOWN
- JSR KEYORDER ;SEE WHICH NEW KEYS PRESSED, OLD KEYS RELEASED, AND
- ; DETERMINE WHICH KEY TO PRESENT TO THE kERNAL
- LDA $033E ;SET UP FOR AND DISPATCH TO kERNAL
- STA $CC
- LDA $033F
- STA $CD
- LDX #$FF
- BIT IGNOREkEYS
- BMI ++
- LDA PREVkEYS+0
- CMP #$FF
- BNE +
- LDA $D3
- BEQ ++
- LDA #88
- + STA $D4
- TAY
- JMP ($033A)
-
- NOkEYpRESSED = * ;NO KEYS PRESSED; SELECT DEFAULT SCAN ROW
- LDA #$7F
- STA PA
- LDA #$FF
- STA PK
-
- JOYSTICKpRESSED = *
- LDA #$FF ;RECORD THAT NO KEYS ARE DOWN IN OLD 3-KEY ARRAY
- LDX #ROLLOVER-1
- - STA PREVkEYS,X
- DEX
- BPL -
- JSR SCANcAPS ;SCAN THE caps lock KEY
- LDX #$FF
- LDA #0
- STA IGNOREkEYS
-
- + LDA #88 ;PRESENT "NO KEY HELD" TO kERNAL
- STA $D4
- TAY
- JMP $C697
-
- INITIALiNSTALL = * ;INSTALL WEDGE: SET RESTORE VECTOR, PRINT MESSAGE
- JSR INSTALL
- LDA #<REINSTALL
- LDY #>REINSTALL
- STA $0A00
- STY $0A01
- LDX #0
- - LDA INSTALLmSG,X
- BEQ +
- JSR $FFD2
- INX
- BNE -
- + RTS
-
- INSTALLmSG = *
- .BYTE 13
- .ASC "KEYSCAN128 INSTALLED"
- .BYTE 0
-
- REINSTALL = * ;RE-INSTALL WEDGE AFTER A run/stop+restore
- JSR INSTALL
- JMP $4003
-
- INSTALL = * ;GUTS OF INSTALLATION: SET irq VECTOR TO PATCH CODE
- SEI ; AND INITIALIZE SCANNING VARIABLES
- LDA #<IRQ
- LDY #>IRQ
- STA $0314
- STY $0315
- CLI
- LDX #ROLLOVER-1
- LDA #$FF
- - STA PREVkEYS,X
- DEX
- BPL -
- LDA #0
- STA IGNOREkEYS
- RTS
-
- MASK = $CC
-
- KEYSCAN = * ;SCAN THE (EXTENDED) KEYBOARD USING THE FORWARD
- LDX #$FF ; ROW-WISE "SLOW" TECHNIQUE
- LDY #$FF
- LDA #$FE
- STA MASK+0
- LDA #$FF
- STA MASK+1
- JMP +
- NEXTrOW = *
- - LDA PB
- CMP PB
- BNE -
- STY PA
- STY PK
- EOR #$FF
- STA SCANtABLE,X
- SEC
- ROL MASK+0
- ROL MASK+1
- + LDA MASK+0
- STA PA
- LDA MASK+1
- STA PK
- INX
- CPX #SCANROWS
- BCC NEXTrOW
- RTS
-
- SHIFTvALUE = $D3
-
- SHIFTrOWS .BYTE $01,$06,$07,$07,$0A
- SHIFTbITS .BYTE $80,$10,$20,$04,$01
- SHIFTmASK .BYTE $01,$01,$02,$04,$08
-
- SHIFTDECODE = * ;SEE WHICH "SHIFT" KEYS ARE HELD DOWN, PUT THEM INTO
- JSR SCANcAPS ; PROPER POSITIONS IN $d3 (SHIFT FLAGS), AND REMOVE
- LDY #4 ; THEM FROM THE SCAN MATRIX
- - LDX SHIFTrOWS,Y
- LDA SCANtABLE,X
- AND SHIFTbITS,Y
- BEQ +
- LDA SHIFTmASK,Y
- ORA SHIFTvALUE
- STA SHIFTvALUE
- LDA SHIFTbITS,Y
- EOR #$FF
- AND SCANtABLE,X
- STA SCANtABLE,X
- + DEY
- BPL -
- RTS
-
- SCANcAPS = * ;SCAN THE caps lock KEY FROM THE PROCESSOR i/o PORT
- - LDA $1
- CMP $1
- BNE -
- EOR #$FF
- AND #$40
- LSR
- LSR
- STA SHIFTvALUE
- RTS
-
- NEWPOS = $CC
- KEYCODE = $D4
- XSAVE = $CD
-
- KEYDECODE = * ;GET THE SCAN CODES OF THE FIRST THREE KEYS HELD DOWN
- LDX #ROLLOVER-1 ;INITIALIZE: $FF MEANS NO KEY HELD
- LDA #$FF
- - STA NEWkEYS,X
- DEX
- BPL -
- LDY #0
- STY NEWPOS
- LDX #0
- STX KEYCODE
-
- DECODEnEXTrOW = * ;DECODE A ROW, INCREMENTING THE CURRENT SCAN CODE
- LDA SCANtABLE,X
- BEQ DECODEcONTINUE
- ;AT THIS POINT, WE KNOW THAT THE ROW HAS A KEY HELD
- LDY KEYCODE
- - LSR
- BCC ++
- PHA ;HERE WE KNOW WHICH KEY IT IS, SO STORE ITS SCAN CODE,
- STX XSAVE ; UP TO 3 KEYS
- LDX NEWPOS
- CPX #ROLLOVER
- BCS +
- TYA
- STA NEWkEYS,X
- INC NEWPOS
- + LDX XSAVE
- PLA
- + INY
- CMP #$00
- BNE -
-
- DECODEcONTINUE = *
- CLC
- LDA KEYCODE
- ADC #8
- STA KEYCODE
- INX
- CPX #SCANROWS
- BCC DECODEnEXTrOW
- RTS
-
- ;KEYORDER: DETERMINE WHAT KEY TO PRESENT TO THE kERNAL AS BEING LOGICALLY THE
- ;ONLY ONE PRESSED, BASED ON WHICH KEYS PREVIOUSLY HELD HAVE BEEN RELEASED AND
- ;WHICH NEW KEYS HAVE JUST BEEN PRESSED
-
- KEYORDER = *
- ;** REMOVE OLD KEYS NO LONGER HELD FROM OLD SCAN CODE ARRAY
- LDY #0
- NEXTrEMOVE = *
- LDA PREVkEYS,Y ;GET CURRENT OLD KEY
- CMP #$FF
- BEQ ++
- LDX #ROLLOVER-1 ;SEARCH FOR IT IN THE NEW SCAN CODE ARRAY
- - CMP NEWkEYS,X
- BEQ +
- DEX
- BPL -
- TYA ;HERE, OLD KEY NO LONGER HELD; REMOVE IT
- TAX
- - LDA PREVkEYS+1,X
- STA PREVkEYS+0,X
- INX
- CPX #ROLLOVER-1
- BCC -
- LDA #$FF
- STA PREVkEYS+ROLLOVER-1
- STA IGNOREkEYS
- + INY ;CHECK NEXT OLD KEY
- CPY #ROLLOVER
- BCC NEXTrEMOVE
-
- ;** INSERT NEW KEYS AT FRONT OF OLD SCAN CODE ARRAY
- + LDY #0
- NEXTiNSERT = *
- LDA NEWkEYS,Y ;GET CURRENT NEW KEY
- CMP #$FF
- BEQ ++
- LDX #ROLLOVER-1 ;CHECK OLD SCAN CODE ARRAY FOR IT
- - CMP PREVkEYS,X
- BEQ +
- DEX
- BPL -
- PHA ;IT'S NOT THERE, SO INSERT NEW KEY AT FRONT, EXIT
- LDX #ROLLOVER-2
- - LDA PREVkEYS+0,X
- STA PREVkEYS+1,X
- DEX
- BPL -
- LDA #0
- STA IGNOREkEYS
- PLA
- STA PREVkEYS+0
- LDY #ROLLOVER ;(TRICK TO EXIT)
- + INY
- CPY #ROLLOVER
- BCC NEXTiNSERT
- + RTS ;NOW, THE HEAD OF THE OLD SCAN CODE ARRAY CONTAINS
- ; THE SCAN CODE TO PRESENT TO THE kERNAL, AND OTHER
- ; POSITIONS REPRESENT KEYS THAT ARE ALSO HELD DOWN
- ; THAT HAVE ALREADY BEEN PROCESSED AND THEREFORE CAN
- ; BE IGNORED UNTIL THEY ARE RELEASED
-
- CHECKjOYSTICK = * ;CHECK IF JOYSTICK IS PUSHED: UN-SELECT ALL KEYBOARD
- LDA #$FF ; ROWS AND SEE IF THERE ARE ANY "0"S IN THE SCAN
- STA PA ; STATUS REGISTER
- STA PK
- - LDA PB
- CMP PB
- BNE -
- CMP #$FF
- LDA #$7F ;RESTORE TO DEFAULT kERNAL ROW SELECTED (TO THE ONE
- STA PA ; CONTAINING THE stop KEY)
- LDA #$FF
- STA PK
- RTS
-
- ;GLOBAL VARIABLES
-
- SCANtABLE .BUF SCANROWS ;VALUES OF THE ELEVEN KEYBOARD SCAN ROWS
- NEWkEYS .BUF ROLLOVER ;CODES OF UP TO THREE KEYS HELD SIMULTANEOUSLY
- IGNOREkEYS .BUF 1 ;FLAG: IF AN OLD KEY HAS BEEN RELEASED AND NO
- ; NEW KEY HAS BEEN PRESSED, STOP ALL KEY
- ; REPEATING
- PREVkEYS .BUF ROLLOVER+2 ;KEYS HELD ON PREVIOUS SCAN
- -----=-----
-
- aND THAT'S ALL THERE IS TO IT. :-)
-
- 5. the c-64 keyscanner
-
- tHE BOOT PROGRAM FOR THE c-64 KEYSCANNER IS AS FOLLOWS:
-
- 10 D=PEEK(186)
- 20 IF A=1 THEN 60
- 30 A=1
- 40 LOAD"KEYSCAN64",D,1
- 50 GOTO 10
- 60 SYS 49152+5*256 : REM $C500
-
- iT IS VERY MUCH LIKE BOOT PROGRAMS FOR OTHER MACHINE LANGUAGE PROGRAMS THAT
- DON'T LOAD AT THE START OF basic. iT WILL LOAD THE BINARY FROM THE LAST
- DEVICE ACCESSED, AND ACTIVATE IT.
-
- a LISTING OF THE c-64 KEYSCANNING CODE IS NOT PRESENTED HERE BECAUSE IT IS SO
- SIMILAR TO THE c-128 LISTING. tHE ONLY THINGS THAT ARE DIFFERENT ARE THE
- kERNAL PATCHES AND THE KEYBOARD SCANNING (BECAUSE THE THREE EXTRA ROWS DON'T
- HAVE TO BE SCANNED). tHE irq HAD TO BE SUBSTANTIALLY COPIED FROM THE rom,
- AGAIN, TO GET AT THE CALL TO THE KEY SCANNING. aLSO, RATHER THAN TAKING
- OVER THE basic RESET VECTOR (SINCE THERE ISN'T ONE), THE nmi VECTOR IS
- TAKEN OVER TO INSURE THE SURVIVAL OF THE KEY SCANNER AFTER A run/stop+restore.
- a BIT OF ITS PREAMBLE ALSO HAD TO BE COPIED OUT OF rom TO GET AT THE GOOD
- STUFF. iF YOU WANT A COPY OF THE c-64 LISTING, YOU CAN E-MAIL ME.
-
- 6. uuencoded files
-
- hERE ARE THE BINARY EXECUTABLES IN UUENCODED FORM. tHE crc32S OF THE FOUR
- FILES ARE AS FOLLOWS:
-
- CRC32 = 3398956287 FOR "KEYSCAN128"
- CRC32 = 2301926894 FOR "KEYSCAN64.BOOT"
- CRC32 = 1767081474 FOR "KEYSCAN64"
- CRC32 = 1604419896 FOR "KEYSHOW"
-
- BEGIN 640 KEYSCAN128
- m{$60}!5,'a;8({$60}h53&gz.*t9t"d!\{$60}>-&="ev,g_\&\l$={$60}p!"e{$60}t#$xi=cp+"38
- m4{$60}:m-{$60}j-$m"e{$60}2g]"01(k2t*2*t1t"e_"2"hk1;0)-@p{$60}rgo+{$60}d0jm{$60}hj?^-
- m$m"e{$60}0d"*?l%v4bm+{$60}i(k1'0*5^hk1;0*>^jl{$60}bb!\k0_>kjjfb-&-!ha0&,
- m$=".%m"p$ztpt"d!\{$60}rev"e{$60}\{$60}:m$={$60}0{$60}3a8d{$60}<@ja4@y\8x8*d{$60}c0#<c2_0
- mk0'<s0'<t/c)__{$60}z((d7d#\@<18@b1>0-r"w%b#l%b{$60}l%zt^{$60}x7,k3\#a<vb
- m_rrt%s{$60}qk;47r?_0!j73\":i6(74j&pz{$60}ze_c0#<j?^-+]"i_z("g;47ra#z
- m(-t6ho^i{$60}(vt%ze8a=2h3)?&(%46j4^@%ht{$60}"hp!"j({$60}o3d6\{$60}8@to_ht/5@
- m#4m%65-#04xq,c@@24y35$%,3$5${$60}"!5%dp#0'bi{$60}z{$60}5c10#c!4#6*("j?^=
- mm1?*$/ji{$60}(vt%v"b_z#_j?z%s*g_a<u,f!:m{$60}=s-{$60}=s0^(p{$60}w(pot$g_g:87
- m.";,)lves(t{$60}w*7-c2_0z.{$60}+d-e@{$60}08'!pj{$60}$"{$60}${$60}0$!{$60}@0((-t6h{$60}2^j!:]
- mia<yk1;p$kfr%@73a=.yk19)_svf%yvf%x@0x&"e{$60}<4!t/i)_re{$60}2dj%tv"b
- m{$60}jg_g;$7ra#zh{$60}"$s*({$60}am2]ia?p'*342i{$60}22(;-ils@{$60}[{$60}&f)vq%^;,iluh
- mr,d{$60}t.88i=1i"(74z.{$60}+d--@h{$60}"ym1?)__{$60}dh@+=l1?p&,h0^)bjo;87g;47
- mz.{$60}"d/6i_xvw%xvt%\c{$60}{$60}y#5h{$60}"yl1?)__{$60}fh@+=m1?p&lh0^$bb{$60};vu%yvv
- m%\h0]zd{$60}c;07:(vu%z{$60}#r,{$60}#d--@j?^-{$60}-r-+]"m{$60}=s-{$60}=s0^,g_j7^-{$60}-ri
- 9_xtot&{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}
- {$60}
- END
- BEGIN 640 KEYSCAN64.BOOT
- m{$60}0@."{$60}h{$60}1++"*#$x-bd{$60}'0@4{$60}(l@0;(q(*<@-c{$60}{$60})0@>{$60}$&r,0{$60}z""@{$60}dr)+
- m15e30t%.-c0b+$0l,0!#"#({$60}b2{$60}q,{$60}!@"#p{$60}gb{$60}t.3$u,jhuk#(u-b{$60}@.b"/
- )("1#-3{$60}p{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}
- {$60}
- END
- BEGIN 640 KEYSCAN64
- m{$60},5,q,5,$,9,zl4@zo^es-{$60}iqlw0):d4a<vdtt;/kh<"l=&p$>;/a<x@).jq
- m\xv'{$60}jz&{$60}j7.28{$60}@'.je{$60}2d0\{$60}j@{$60}(3{$60}i0$)(-{$60}(i<#0!j4!*1^%{$60}2!9q4q^
- mzjd{$60}c0#<k0'<s0'<t/c)__{$60}y(%c'd#d@6,8@6,>0,2"-qb"[qb#[qjf!a?6i
- mzx7vho\l>,<p+:uyq\g_t{$60}>mc0+p(:e{$60}a<nh;(\"j7^-{$60}-ri_z("g7g'ra#z
- m(+7&ho^i{$60}(uxqze{$60}a<nh3";k(.k%h@"]u<7p!b#2_^c0]6!+15e30t%.-c0@
- m24y35$%,3$5$#0!xj0f@q8t4{$60}xp5{$60}zdgh,:-&{$60}.,&0-8h@*i_yuyq\h0^jd{$60}
- mc7c'8'bi,:#jc10#c!4#j4>@_ht8{$60}xp9{$60}ua@2(i(f$bi?xt-w:p-w3{$60}?({$60}+]
- mt{$60}-l{$60}h{$60}@o/8@x?_0#r{$60}5_2"c_2{$60}8y2#jq6p"h$qr_j+_h/^i_h7u3';&k0'<
- ms0'<t/b,{$60}-q)_yumqs@f]:7uc0#<z.{$60}(d.-@{$60}08'!x{$60}0({$60}0!{$60}0($(+7&h{$60}.^
- m@<:];<<ya<;p%+f)q@v-{$60}hv-{$60}kf%qdg_/6w'g6w'b!#>8*d{$60}c8t"8*("j?^=
- m=<?*$/j@{$60}(3uh@"&r[umq_{$60}<i,m*d!)(ao:f]>{$60}#l{$60}:8g77'yo6f]fc(r0#0
- myabervd(a<ohx{$60}b0tv"@{$60}+eyq\g_\"2b{$60}muuq_{$60}8ra#xf*j]>l>=><?hx{$60}*0
- m]:g_c7o'c7c'r,{$60}#d-6@{$60}+euq\g_\":b{$60}muyq_{$60}:ra#x2*(!o7g'g7k'ra#w
- mj0"->,=hc7g'h{$60}/(p{$60}.0tv"i_xt{$60}w*t!w,t!w-#xr?^i?xt{$60}w&{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}
- *{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}
- {$60}
- END
- BEGIN 640 KEYSHOW
- m{$60}!-,"q,{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}*f3(-+_(#83h@{$60}@%q0@5a.b"b{$60}7%"!t$z(4(!<4(/03
- mhax@%q0@3!1,$!-xh@"i_ht{$60}w*t!w,t!w-#x2?^={$60}q,x+@#<z.{$60}(d.i88'bb
- m!ze_c0#<k0'<s0'<t/a)_yt#$san{$60}-s*$.q88'bi{$60}(t"w*g_c0/<h{$60}>i?x4#
- mi0.-{$60}=rm{$60}-s-{$60}-s0^*+_c@'<2?^b!ph^{$60}q/*$/dx9@.($-vi_xt"w*d{$60}c0/<
- m6&!xj0"-{$60}mri_xt#w*{$60}'j7^%{$60}z4#c0'<k0#<s0#<t/bb_xx!w$g_h@<*/@,3
- mra#y.&8#b!#=j?^-{$60}mri{$60}(t#w%a@>*({$60}j?z%{$60}z4#c0#<k0'<s0'<t/a)_yt#
- m$s@f{$60}^c@")#f6&"@!(8$a{$60}6b{$60}+t#$r{$60}v%!be!&dha020{$60}n8%z.{$60}(d.i@a0*@
- 6!t8"j0!i,)$$b!#u8{$60}{$60}{$60}{$60}{$60}{$60}{$60}{$60}*({$60}8{$60}{$60}{$60}
- {$60}
- END
-