home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / enterprs / c128 / text / hacking6.arc / KEYSCAN.TXT < prev    next >
Text File  |  1993-09-16  |  31KB  |  728 lines

  1. ===============================================================================
  2. THREE-KEY ROLLOVER for the C-128 and C-64.
  3. by Craig Bruce  <csbruce@neumann.uwaterloo.ca>
  4.  
  5. 1. INTRODUCTION
  6.  
  7. This article examines a three-key rollover mechanism for the keyboards of the
  8. C-128 and C-64 and presents Kernal-wedge implementations for both machines.
  9. Webster's doesn't seem to know, so I'll tell you that this means that the
  10. machine will act sensibly if you are holding down one key and then press
  11. another without releasing the first (or even press a third key while holding
  12. down two others).  This is useful to fast touch typers.  In fact, fast typing
  13. without rollover can be quite annoying; you get a lot of missing letters.
  14.  
  15. Another annoying property of the kernel keyscanning is joystick interference.
  16. If you move the joystick plugged into port #1, you will notice that some junk
  17. keystrokes result.  The keyscanners here eliminate this problem by simply
  18. checking if the joystick is pressed and ignoring the keyboard if it is.
  19.  
  20. The reason that a 3-key rollover is implemented instead of the more general
  21. N-key rollover is that scanning the keyboard becomes more and more unreliable
  22. as more keys are held down.  Key "shaddows" begin to appear to make it look
  23. like you are holding down a certain key when you really are not.  So, by
  24. limiting the number of keys scanned to 3, some of this can be avoided.  You
  25. will get strange results if you hold down more than three keys at a time, and
  26. even sometimes when holding down 3 or less.  The "shift" keys (Shift,
  27. Commodore, Control, Alternate, and CapsLock) don't count in the 3 keys of
  28. rollover, but they do make the keyboard harder to read correctly.
  29. Fortunately, three keys will allow you to type words like "AND" and "THE"
  30. without releasing any keys.
  31.  
  32. 2. USER GUIDE
  33.  
  34. Using these utilities is really easy - you just type away like normal.  To
  35. install the C-128 version, enter:
  36.  
  37. BOOT "KEYSCAN128"
  38.  
  39. and you're in business.  The program will display "Keyscan128 installed" and
  40. go to work.  The program loads into memory at addresses $1500-$17BA (5376-6074
  41. decimal), so you'll want to watch out for conflicts with other utilities.
  42. This program also takes over the IRQ vector and the BASIC restart vector
  43. ($A00).  The program will survive a RUN/STOP+RESTORE.  To uninstall this
  44. program, you must reset the machine (or poke the kernel values back into the
  45. vectors); it does not uninstall itself.
  46.  
  47. Loading the C-64 version is a bit trickier, so a small BASIC loader program is
  48. provided.  LOAD and RUN the "KEYSCAN64.BOOT" program.  It will load the
  49. "KEYSCAN64" program into memory at addresses $C500-$C77E (50432-51070 decimal)
  50. and execute it (with a SYS 50432).  To uninstall the program, enter SYS 50435.
  51. The program takes over the IRQ and NMI vectors and only gives them back to the
  52. kernel upon uninstallation.  The program will survive a RUN/STOP+RESTORE.
  53.  
  54. Something that you may or may not know about the C-64 is that its keys can be
  55. made to repeat by poking to address 650 decimal.  POKE650,128 will enable the
  56. repeating of all keys.  POKE650,0 will enable only the repeating of the SPACE,
  57. DELETE, and CURSOR keys.  POKE650,64 will disable the repeating of all keys.
  58. An unusual side effect of changing this to either full repeat or no repeat is
  59. that holding down SHIFT+COMMODORE (character set shift) will repeat rapidly.
  60.  
  61. To see the rollover in action, hold down the "J" key for a while, and then
  62. press "K" without releasing "J".  "K" will come out as expected, as it would
  63. with the kernal.  Now, release the "J" key.  If you are on a C-128, you will
  64. notice that the "K" key will now stop repeating (this is actually an important
  65. feature - it avoids problems if you don't release all of the keys you are
  66. holding down, at once).  Now, press and hold the "J" key again without
  67. releasing the "K".  "J" will now appear.  It wouldn't using the Kernal key
  68. scanner.  You can also try this with 3-key combinations.  There will be some
  69. combinations that cause problems; more on this below.
  70.  
  71. Also, take a spaz on the joystick plugged into port #1 and observe that no
  72. garbage gets typed in.  This was an annoying problem with the kernel of both
  73. the 64 and 128 and has lead many different games to picking between joystick
  74. #1 and #2 as the primary controller.  The joystick in port #2 is not a problem
  75. to either Keyscan-128/64 or the Kernal.
  76.  
  77. 3. KEYBOARD SCANNING
  78.  
  79. The Kernal scans the keyboard sixty times a second to see what keys you are
  80. holding down.  Because of hardware peculiarities, there are multiple scanning
  81. techniques that will give different results.
  82.  
  83. 3.1. SCANNING EXAMPLE
  84.  
  85. An example program is included to demonstrate different keyboard scanning
  86. techniques possible.  To run it from a C-128 in 40-column (slow) mode, enter:
  87.  
  88. BOOT "KEYSHOW"
  89.  
  90. On a C-64, you must:
  91.  
  92. LOAD "KEYSHOW",8,1
  93.  
  94. and then:
  95.  
  96. SYS 4864
  97.  
  98. The same program works on both machines.  Four maps of the keyscanning matrix
  99. will be displayed on the 40-column screen, as scanned by different techniques.
  100. The leftmost one is scanned from top to bottom "quickly".  The second from the
  101. left scans from bottom to top "quickly".  The third from the left scans the
  102. keyboard sideways, and the rightmost matrix scans the keys from top to bottom
  103. "slowly".
  104.  
  105. The mapping of keyscan matrix positions to keys is as follows:
  106.  
  107. ROWS: \             COLUMNS:    peek($DC01)
  108. poke   \
  109. $DC00   \   128      64      32      16      8       4       2       1
  110.          +-------+-------+-------+-------+-------+-------+-------+-------+
  111.   255-1  | DOWN  |   F5  |   F3  |   F1  |   F7  | RIGHT | RETURN| DELETE|
  112.          +-------+-------+-------+-------+-------+-------+-------+-------+
  113.   255-2  |LEFT-SH|   E   |   S   |   Z   |   4   |   A   |   W   |   3   |
  114.          +-------+-------+-------+-------+-------+-------+-------+-------+
  115.   255-4  |   X   |   T   |   F   |   C   |   6   |   D   |   R   |   5   |
  116.          +-------+-------+-------+-------+-------+-------+-------+-------+
  117.   255-8  |   V   |   U   |   H   |   B   |   8   |   G   |   Y   |   7   |
  118.          +-------+-------+-------+-------+-------+-------+-------+-------+
  119.  255-16  |   N   |   O   |   K   |   M   |   0   |   J   |   I   |   9   |
  120.          +-------+-------+-------+-------+-------+-------+-------+-------+
  121.  255-32  |   ,   |   @   |   :   |   .   |   -   |   L   |   P   |   +   |
  122.          +-------+-------+-------+-------+-------+-------+-------+-------+
  123.  255-64  |   /   |   ^   |   =   |RGHT-SH|  HOME |   ;   |   *   |   \   |
  124.          +-------+-------+-------+-------+-------+-------+-------+-------+
  125. 255-128  | STOP  |   Q   |COMMODR| SPACE |   2   |CONTROL|   _   |   1   |
  126.          +-------+-------+-------+-------+-------+-------+-------+-------+
  127.  
  128. The following table contains the additional keys which must be scanned on the
  129. C128 (but which are not displayed by the example scanning program).
  130.  
  131. ROWS: \               COLUMNS:    peek($DC01)
  132. poke   \
  133. $D02F   \   128      64      32      16      8       4       2       1
  134.          +-------+-------+-------+-------+-------+-------+-------+-------+
  135.   255-1  |   1   |   7   |   4   |   2   |  TAB  |   5   |   8   |  HELP |
  136.          +-------+-------+-------+-------+-------+-------+-------+-------+
  137.   255-2  |   3   |   9   |   6   | ENTER |   LF  |   -   |   +   |  ESC  |
  138.          +-------+-------+-------+-------+-------+-------+-------+-------+
  139.   255-4  |NO-SCRL| RIGHT |  LEFT |  DOWN |   UP  |   .   |   0   |  ALT  |
  140.          +-------+-------+-------+-------+-------+-------+-------+-------+
  141.  
  142. These tables are presented on page 642 of the Commodore 128 Programmer's
  143. Reference Guide.  The scan codes that are stored in location 212 on the C128
  144. and location 197 on the C64 are calculated based on the above tables.  The
  145. entry in the "1" bit position of the first line of the first table (DELETE)
  146. has a scan code of 0, the "2" entry (RETURN) has a scan code of 1, etc., the
  147. entry on the second scan line in the "1" position ("3") has a scan code of 8,
  148. etc., all the way down to code 63.  The scan codes for the 128 go all the way
  149. to 87, continuing in the second table like the first.
  150.  
  151. You will notice some strange effects of the different scanning techniques when
  152. you hold down multiple keys.  More on this below.  Also try pushing joystick
  153. #1 around.
  154.  
  155. 3.2. SCANNING HARDWARE
  156.  
  157. To scan the 128 keyboard, you must poke a value into $DC00 (CIA#1 port A) and
  158. $D02F (VIC chip keyboard select port) to select the row to be scanned.  The
  159. Data Direction Register for this port will be set to all outputs by the
  160. Kernal, so you don't have to worry about it.  Each bit of $DC00 and the three
  161. least significant bits of $D02F are used for selecting rows.  A "0" bit means
  162. that a row IS selected, and a "1" means that a row IS NOT selected.  The poke
  163. value to use for selecting among the various rows are given in the two tables
  164. in the previous section.
  165.  
  166. Using one bit per row allows you to select multiple rows at the same time.  It
  167. can be useful to select all rows at one time or no rows.  To read the row that
  168. has been selected, simply peek at location $DC01 (CIA#1 port B).  Each bit
  169. will tell you whether the corresponding key is currently being held down or
  170. not.  Again, we have reverse logic; a "0" means that the key is being held
  171. down, and a "1" means that the key is not held down.  The bit values
  172. corresponding to the keys are given as the column headings in the tables in
  173. the previous section.  Since there is no such thing as a perfect mechanical
  174. switch, it is recommended that you "debounce" each key row read in the
  175. following way:
  176.  
  177. again:
  178.    lda $dc01
  179.    cmp $dc01
  180.    bne again
  181.  
  182. So, to scan the entire keyboard, you simply select each scan row in some
  183. order, and read and remember the keys held down on the row.  As it turns out,
  184. you have to be a bit careful of exactly how you "select" a row.  Also, there
  185. is a shortcut that you can take.  In order to find out if any key is being
  186. held down in one operation, all you have to do is select all rows at once and
  187. see if there are any "0" bits in the read value.  If so, there is a key being
  188. held down somewhere; if not, then there is no key being held down, so you
  189. don't have to bother scanning the entire keyboard.  This will reduce our
  190. keyscanning significantly, which is important, since the keyboard will be
  191. scanned every 1/60 of a second.
  192.  
  193. As mentioned above, joystick #1 will interfere with the Kernal reading the
  194. keyboard.  This is because the read value of joystick #1 is wired into CIA#1
  195. port A, the same place that the keyboard read is wired in.  So, whenever a
  196. switch in the joystick is pushed, the corresponding bit of the keyboard scan
  197. register will be forced to "0", regardless of which keys are pressed and
  198. regardless of which scan row is selected.  There's the catch.  If we were to
  199. un-select all scan rows and still notice "0"s in the keyboard read register,
  200. then we would know that the joystick was being pushed and would interfere with
  201. our keyboard scanning, so we could abort keyboard scanning and handle this
  202. case as if no keys were being held down.
  203.  
  204. It still would be possible but unlikely that the user could push the joystick
  205. in the middle of us scanning the keyboard and screw up our results, so to
  206. defend against this, we check for the joystick being pushed both before and
  207. after scanning the keyboard.  If we find that the joystick is pushed at either
  208. of these times, then we throw out the results and assume that no keys are held
  209. down.  This way, the only way that a user could screw up the scanning is if
  210. he/she/it were to press a switch after we begin scanning and release it before
  211. we finish scanning.  Not bloody likely for a human.
  212.  
  213. You get the same deal for keyboard scanning on the 64, except you only need to
  214. use $DC00 for selecting the scan rows.  Also note that you will not be able to
  215. play with keyboard scanning from BASIC because of the interrupt reading of the
  216. keyboard.  You must make sure that interrupts are disabled when playing with
  217. the keyboard hardware, or interrupt scanning can come along at any time and
  218. change all of the register settings.
  219.  
  220. 3.3. SCANNING SOURCE CODE
  221.  
  222. The four keyboard scanning techniques of the example program are presented
  223. below.  The declarations required for all of them are:
  224.  
  225. pa = $dc00        ;row select
  226. pb = $dc01        ;column read
  227. ddra = $dc02      ;ddr for row select
  228. ddrb = $dc03      ;ddr for column read
  229. scanTable .buf 8  ;storage for scan
  230. mask = $03        ;work location
  231.  
  232. The code is as follows, in Buddy format.  Each routine scans the keyboard and
  233. stores the results in the "scanTable" table.
  234.  
  235. ------------------+------------------+------------------+------------------+
  236.  Row forward fast | Row backward fast| Column right     | Row forward slow
  237. ------------------+------------------+------------------+------------------+
  238.   sei             |  sei             |  sei             |  sei
  239.   ldx #0          |  ldx #7          |  lda #$00        |  ldx #0
  240.   lda #$fe        |  lda #$7f        |  sta ddra        |  lda #$fe
  241.   sta pa          |  sta pa          |  lda #$ff        |  sta mask
  242.   nextRow = *     |  nextRow = *     |  sta ddrb        |  nextRow = *
  243. - lda pb          |- lda pb          |  ldy #7          |  lda mask
  244.   cmp pb          |  cmp pb          |  lda #$7f        |  sta pa
  245.   bne -           |  bne -           |  sta mask        |- lda pb
  246.   eor #$ff        |  eor #$ff        |  nextCol = *     |  cmp pb
  247.   sta scanTable,x |  sta scanTable,x |  lda mask        |  bne -
  248.   sec             |  sec             |  sta pb          |  eor #$ff
  249.   rol pa          |  ror pa          |- lda pa          |  sta scanTable,x
  250.   inx             |  dex             |  cmp pa          |  sec
  251.   cpx #8          |  bpl nextRow     |  bne -           |  rol mask
  252.   bcc nextRow     |  cli             |  ldx #$ff        |  inx
  253.   cli             |  rts             |  stx pb          |  cpx #8
  254.   rts             |                  |  eor #$ff        |  bcc nextRow
  255. ------------------+------------------+  ldx #7          |  cli
  256.                                      |- asl             |  rts
  257. The forward "quick" scanning stores  |  rol scanTable,x +------------------+
  258. the scan row selection mask into     |  dex             |
  259. the row selection register and       |  bpl -           |
  260. shifts the "0" bit one position      |  sec             |
  261. "forward" for each row, directly,    |  ror mask        |
  262. using a "rol $dc00" instruction.     |  dey             |
  263. This would probably be the obvious   |  bpl nextCol     |
  264. solution to an optimizing assembler  |  lda #$ff        |
  265. programmer.  However, for some       |  sta ddra        |
  266. reason not quite understood by this  |  lda #$00        |
  267. author, there are "shadowing"        |  sta ddrb        |
  268. problems with this approach.  If     |  cli             |
  269. you were to hold down the two keys   |  rts             |
  270. "H" and "K" at the same time, you    +------------------+
  271. would notice that these two keys
  272. are on the same column of two successive rows.  If you hold them both down,
  273. you will see the two positions become active, but so will the same column of
  274. all successive rows after the "H" and "K", even though these other keys are
  275. not actually held down.  You will get an inaccurate reading if bad keys are
  276. held down simultaneously.  You will notice the use of the term "active" above.
  277. This is because although the hardware returns a "0" for active, the routine
  278. converts that into a "1" for easier processing later.  I am not sure if
  279. everyone will get this same result, but if your keyboard is wired the same as
  280. mine, you will.
  281.  
  282. The backward "quick" scanning operates quite similarly to the forward
  283. scanning, except for the direction of the scan and the direction of the
  284. "shadow"; the shadow goes upwards.  You might think that ANDing together the
  285. results of the forward and backward scan together would eliminate the shadow,
  286. but this will not work since any rows between the rows containing the two keys
  287. held down will be incorrectly read as being active.
  288.  
  289. The columnwise right scanning is the most complicated because the rows must be
  290. converted into columns, to allow the scan matrix to be interpreted as before.
  291. Also, the Data Direction Registers have to be changed.  You might think that
  292. combinging row-wise scanning with columnwise scanning would give better
  293. results, and it probably would, if it weren't for a bizarre hardware problem.
  294. If you hold down two or more keys on the same scan row, say "W" and "E", some
  295. of the keys will flicker or disappear, giving an inaccurate reading.
  296.  
  297. The forward "slow" scanning is the best of the bunch.  Incidentally, it is
  298. what the Kernal uses (as near as I can figure - their code is extremely
  299. convoluted).  This technique is the same as the forward "quick scan," except
  300. that the row selection mask is shifted in a working storage location and poked
  301. into the CIA register, rather than being shifted in place.  I don't know why
  302. this makes a difference, but it does.  There is still a problem with this
  303. technique, but this problem occurs with all techniques.  If you hold down
  304. three keys that form three "corners" of a rectangle in the scanning matrix,
  305. then the missing corner will be read as being held down also.  For example, if
  306. you hold down "C", "N", and "M", then the keyboard hardware will also think
  307. that you are holding down the "X" key.  This is why this article implements a
  308. "three-key" rollover rather than an "N-key" rollover.  Many three-key
  309. combinations will still be interpreted correctly.  Note, however, that shift
  310. keys such as SHIFT or CONTROL will add one more key to the hardware scanning
  311. (but will not be counted in the three-key rollover), making inaccurate results
  312. more likely if you are holding down multiple other keys at the same time.
  313.  
  314. 4. THE C-128 KEYSCANNER
  315.  
  316. This section gives the source code for the C-128 implementation of the
  317. three-key rollover.  The forward "slow" key matrix scanning technique is used,
  318. extended to work with the extra keys of the 128.  It was a bit of a pain
  319. wedging into the Kernal, since there is not a convenient indirect JMP into
  320. scanning the keyboard, like there are for decoding and buffering pressed keys.
  321. A rather lengthy IRQ "preamble" had to be copied from the ROM, up to the
  322. point where it JSRs to the keyscanning routine.  This code in included in
  323. the form of a ".byte" table, to spare you the details.
  324.  
  325. Before scanning the keyboard, we check to see if joystick #1 is pushed and if
  326. a key is actually pressed.  If not, we abort scanning and JMP to the key
  327. repeat handling in the ROM.  If a key is held down, we scan the keyboard and
  328. then examine the result.  First we check for the shift keys (SHIFT, COMMODORE,
  329. CONTROL, ALT, and CAPS LOCK), put them into location $D3 (shift flags) in bit
  330. postitions 1, 2, 4, 8, and 16, respectively, and remove them from the scan
  331. matrix.  The CAPS LOCK key is not on the main key matrix; it is read from the
  332. processor I/O port.  This is good, because otherwise we could not abort
  333. scanning if it were the only key held down.
  334.  
  335. Then we scan the keymatrix for the first three keys that are being held down,
  336. or as many as are held down if less than three.  We store the scan codes of
  337. these keys into a 3-element array.  We also retain a copy of the 3-element
  338. array from the previous scan and we check for different keys being in the two
  339. arrays.  If the old array contains a key that is not present in the new array,
  340. then the use has released a key, so we set a flag to inhibit interpretation of
  341. keys and pretend that no keys are held down.  This is to eliminate undesirable
  342. effects of having other keys held down repeat if you release the most recently
  343. pushed key first.  PC keyboards do this.  This inhibiting will be ignored if
  344. new keys are discovered in the next step.
  345.  
  346. If there are keys in the new array that are not in the old, then the user has
  347. just pressed a new key, so that new key goes to the head of the old array and
  348. we stop comparing the arrays there.  The key in the first position of the old
  349. array is poked into the Kernal "key held down" location for the Kernal to
  350. interpret later.  If more than one new key is discovered at the same time,
  351. then each of the new keys will be picked up on successive keyboard scans and
  352. will be interpreted as just being pushed.  So, if you press the "A", "N", and
  353. "D" keys all at the same time, some permutation of all three of these keys
  354. will appear on the screen.
  355.  
  356. When we are done interpreting the keys, we check the joystick once more and if
  357. it is still inactive, we present the most recently pushed down key to the
  358. Kernal and JMP into the ROM keyboard decoding routine.
  359.  
  360. Unlike in previous issues, this source code is here in literal form; just
  361. extract everything between the "-----=-----"s to nab the source for yourself.
  362. The source is in Buddy assembler format.
  363.  
  364. -----=-----
  365. ;3-Key Rollover-128 by Craig Bruce 18-Jun-93 for C= Hacking magazine
  366.  
  367. .org $1500
  368. .obj "@0:keyscan128"
  369.  
  370. scanrows = 11
  371. rollover = 3
  372.  
  373. pa = $dc00
  374. pb = $dc01
  375. pk = $d02f
  376.  
  377. jmp initialInstall
  378.  
  379. ;ugly IRQ patch code.
  380.  
  381. irq = *  ;$1503
  382.    .byte $d8,$20,$0a,$15,$4c,$69,$fa,$38,$ad,$19,$d0,$29,$01,$f0,$07,$8d
  383.    .byte $19,$d0,$a5,$d8,$c9,$ff,$f0,$6f,$2c,$11,$d0,$30,$04,$29,$40,$d0
  384.    .byte $31,$38,$a5,$d8,$f0,$2c,$24,$d8,$50,$06,$ad,$34,$0a,$8d,$12,$d0
  385.    .byte $a5,$01,$29,$fd,$09,$04,$48,$ad,$2d,$0a,$48,$ad,$11,$d0,$29,$7f
  386.    .byte $09,$20,$a8,$ad,$16,$d0,$24,$d8,$30,$03,$29,$ef,$2c,$09,$10,$aa
  387.    .byte $d0,$28,$a9,$ff,$8d,$12,$d0,$a5,$01,$09,$02,$29,$fb,$05,$d9,$48
  388.    .byte $ad,$2c,$0a,$48,$ad,$11,$d0,$29,$5f,$a8,$ad,$16,$d0,$29,$ef,$aa
  389.    .byte $b0,$08,$a2,$07,$ca,$d0,$fd,$ea,$ea,$aa,$68,$8d,$18,$d0,$68,$85
  390.    .byte $01,$8c,$11,$d0,$8e,$16,$d0,$b0,$13,$ad,$30,$d0,$29,$01,$f0,$0c
  391.    .byte $a5,$d8,$29,$40,$f0,$06,$ad,$11,$d0,$10,$01,$38,$58,$90,$07,$20
  392.    .byte $aa,$15,$20,$e7,$c6,$38,$60
  393.  
  394. ;keyscanning entry point
  395.  
  396. main = *
  397.    lda #0               ;check if any keys are held down
  398.    sta pa
  399.    sta pk
  400. -  lda pb
  401.    cmp pb
  402.    bne -
  403.    cmp #$ff
  404.    beq noKeyPressed     ;if not, then don't scan keyboard, goto Kernal
  405.  
  406.    jsr checkJoystick    ;if so, make sure joystick not pressed
  407.    bcc joystickPressed
  408.    jsr keyscan          ;scan the keyboard and store results
  409.    jsr checkJoystick    ;make sure joystick not pressed again
  410.    bcc joystickPressed
  411.    jsr shiftdecode      ;decode the shift keys
  412.    jsr keydecode        ;decode the first 3 regular keys held down
  413.    jsr keyorder         ;see which new keys pressed, old keys released, and
  414.                         ;  determine which key to present to the Kernal
  415.    lda $033e            ;set up for and dispatch to Kernal
  416.    sta $cc
  417.    lda $033f
  418.    sta $cd
  419.    ldx #$ff
  420.    bit ignoreKeys
  421.    bmi ++
  422.    lda prevKeys+0
  423.    cmp #$ff
  424.    bne +
  425.    lda $d3
  426.    beq ++
  427.    lda #88
  428. +  sta $d4
  429.    tay
  430.    jmp ($033a)
  431.  
  432.    noKeyPressed = *     ;no keys pressed; select default scan row
  433.    lda #$7f
  434.    sta pa
  435.    lda #$ff
  436.    sta pk
  437.  
  438.    joystickPressed = *
  439.    lda #$ff             ;record that no keys are down in old 3-key array
  440.    ldx #rollover-1
  441. -  sta prevKeys,x
  442.    dex
  443.    bpl -
  444.    jsr scanCaps         ;scan the CAPS LOCK key
  445.    ldx #$ff
  446.    lda #0
  447.    sta ignoreKeys
  448.  
  449. +  lda #88              ;present "no key held" to Kernal
  450.    sta $d4
  451.    tay
  452.    jmp $c697
  453.  
  454. initialInstall = *      ;install wedge: set restore vector, print message
  455.    jsr install
  456.    lda #<reinstall
  457.    ldy #>reinstall
  458.    sta $0a00
  459.    sty $0a01
  460.    ldx #0
  461. -  lda installMsg,x
  462.    beq +
  463.    jsr $ffd2
  464.    inx
  465.    bne -
  466. +  rts
  467.  
  468.    installMsg = *
  469.    .byte 13
  470.    .asc "keyscan128 installed"
  471.    .byte 0
  472.  
  473. reinstall = *           ;re-install wedge after a RUN/STOP+RESTORE
  474.    jsr install
  475.    jmp $4003
  476.  
  477. install = *             ;guts of installation: set IRQ vector to patch code
  478.    sei                  ;  and initialize scanning variables
  479.    lda #<irq
  480.    ldy #>irq
  481.    sta $0314
  482.    sty $0315
  483.    cli
  484.    ldx #rollover-1
  485.    lda #$ff
  486. -  sta prevKeys,x
  487.    dex
  488.    bpl -
  489.    lda #0
  490.    sta ignoreKeys
  491.    rts
  492.  
  493. mask = $cc
  494.  
  495. keyscan = *             ;scan the (extended) keyboard using the forward
  496.    ldx #$ff             ;  row-wise "slow" technique
  497.    ldy #$ff
  498.    lda #$fe
  499.    sta mask+0
  500.    lda #$ff
  501.    sta mask+1
  502.    jmp +
  503.    nextRow = *
  504. -  lda pb
  505.    cmp pb
  506.    bne -
  507.    sty pa
  508.    sty pk
  509.    eor #$ff
  510.    sta scanTable,x
  511.    sec
  512.    rol mask+0
  513.    rol mask+1
  514. +  lda mask+0
  515.    sta pa
  516.    lda mask+1
  517.    sta pk
  518.    inx
  519.    cpx #scanrows
  520.    bcc nextRow
  521.    rts
  522.  
  523. shiftValue = $d3
  524.  
  525. shiftRows .byte $01,$06,$07,$07,$0a
  526. shiftBits .byte $80,$10,$20,$04,$01
  527. shiftMask .byte $01,$01,$02,$04,$08
  528.  
  529. shiftdecode = *         ;see which "shift" keys are held down, put them into
  530.    jsr scanCaps         ;  proper positions in $D3 (shift flags), and remove
  531.    ldy #4               ;  them from the scan matrix
  532. -  ldx shiftRows,y
  533.    lda scanTable,x
  534.    and shiftBits,y
  535.    beq +
  536.    lda shiftMask,y
  537.    ora shiftValue
  538.    sta shiftValue
  539.    lda shiftBits,y
  540.    eor #$ff
  541.    and scanTable,x
  542.    sta scanTable,x
  543. +  dey
  544.    bpl -
  545.    rts
  546.  
  547. scanCaps = *            ;scan the CAPS LOCK key from the processor I/O port
  548. -  lda $1
  549.    cmp $1
  550.    bne -
  551.    eor #$ff
  552.    and #$40
  553.    lsr
  554.    lsr
  555.    sta shiftValue
  556.    rts
  557.  
  558. newpos = $cc
  559. keycode = $d4
  560. xsave = $cd
  561.  
  562. keydecode = *           ;get the scan codes of the first three keys held down
  563.    ldx #rollover-1      ;initialize: $ff means no key held
  564.    lda #$ff
  565. -  sta newKeys,x
  566.    dex
  567.    bpl -
  568.    ldy #0
  569.    sty newpos
  570.    ldx #0
  571.    stx keycode
  572.  
  573.    decodeNextRow = *    ;decode a row, incrementing the current scan code
  574.    lda scanTable,x
  575.    beq decodeContinue
  576.                         ;at this point, we know that the row has a key held
  577.    ldy keycode
  578. -  lsr
  579.    bcc ++
  580.    pha                  ;here we know which key it is, so store its scan code,
  581.    stx xsave            ;  up to 3 keys
  582.    ldx newpos
  583.    cpx #rollover
  584.    bcs +
  585.    tya
  586.    sta newKeys,x
  587.    inc newpos
  588. +  ldx xsave
  589.    pla
  590. +  iny
  591.    cmp #$00
  592.    bne -
  593.  
  594.    decodeContinue = *
  595.    clc
  596.    lda keycode
  597.    adc #8
  598.    sta keycode
  599.    inx
  600.    cpx #scanrows
  601.    bcc decodeNextRow
  602.    rts
  603.  
  604. ;keyorder: determine what key to present to the Kernal as being logically the
  605. ;only one pressed, based on which keys previously held have been released and
  606. ;which new keys have just been pressed
  607.  
  608. keyorder = *
  609.    ;** remove old keys no longer held from old scan code array
  610.    ldy #0
  611.    nextRemove = *
  612.    lda prevKeys,y       ;get current old key
  613.    cmp #$ff
  614.    beq ++
  615.    ldx #rollover-1      ;search for it in the new scan code array
  616. -  cmp newKeys,x
  617.    beq +
  618.    dex
  619.    bpl -
  620.    tya                  ;here, old key no longer held; remove it
  621.    tax
  622. -  lda prevKeys+1,x
  623.    sta prevKeys+0,x
  624.    inx
  625.    cpx #rollover-1
  626.    bcc -
  627.    lda #$ff
  628.    sta prevKeys+rollover-1
  629.    sta ignoreKeys
  630. +  iny                  ;check next old key
  631.    cpy #rollover
  632.    bcc nextRemove
  633.  
  634.    ;** insert new keys at front of old scan code array 
  635. +  ldy #0
  636.    nextInsert = *
  637.    lda newKeys,y        ;get current new key
  638.    cmp #$ff
  639.    beq ++
  640.    ldx #rollover-1      ;check old scan code array for it
  641. -  cmp prevKeys,x
  642.    beq +
  643.    dex
  644.    bpl -
  645.    pha                  ;it's not there, so insert new key at front, exit
  646.    ldx #rollover-2
  647. -  lda prevKeys+0,x
  648.    sta prevKeys+1,x
  649.    dex
  650.    bpl -
  651.    lda #0
  652.    sta ignoreKeys
  653.    pla
  654.    sta prevKeys+0
  655.    ldy #rollover        ;(trick to exit)
  656. +  iny
  657.    cpy #rollover
  658.    bcc nextInsert
  659. +  rts                  ;now, the head of the old scan code array contains
  660.                         ;  the scan code to present to the Kernal, and other
  661.                         ;  positions represent keys that are also held down
  662.                         ;  that have already been processed and therefore can
  663.                         ;  be ignored until they are released
  664.  
  665. checkJoystick = *       ;check if joystick is pushed: un-select all keyboard
  666.    lda #$ff             ;  rows and see if there are any "0"s in the scan
  667.    sta pa               ;  status register
  668.    sta pk
  669. -  lda pb
  670.    cmp pb
  671.    bne -
  672.    cmp #$ff
  673.    lda #$7f             ;restore to default Kernal row selected (to the one
  674.    sta pa               ;  containing the STOP key)
  675.    lda #$ff
  676.    sta pk
  677.    rts
  678.  
  679. ;global variables
  680.  
  681. scanTable  .buf scanrows        ;values of the eleven keyboard scan rows
  682. newKeys    .buf rollover        ;codes of up to three keys held simultaneously
  683. ignoreKeys .buf 1               ;flag: if an old key has been released and no
  684.                                 ;  new key has been pressed, stop all key
  685.                                 ;  repeating
  686. prevKeys   .buf rollover+2      ;keys held on previous scan
  687. -----=-----
  688.  
  689. And that's all there is to it.  :-)
  690.  
  691. 5. THE C-64 KEYSCANNER
  692.  
  693. The boot program for the C-64 keyscanner is as follows:
  694.  
  695. 10 d=peek(186)
  696. 20 if a=1 then 60
  697. 30 a=1
  698. 40 load"keyscan64",d,1
  699. 50 goto 10
  700. 60 sys 49152+5*256  : rem $c500
  701.  
  702. It is very much like boot programs for other machine language programs that
  703. don't load at the start of BASIC.  It will load the binary from the last
  704. device accessed, and activate it.
  705.  
  706. A listing of the C-64 keyscanning code is not presented here because it is so
  707. similar to the C-128 listing.  The only things that are different are the
  708. Kernal patches and the keyboard scanning (because the three extra rows don't
  709. have to be scanned).  The IRQ had to be substantially copied from the ROM,
  710. again, to get at the call to the key scanning.  Also, rather than taking
  711. over the BASIC reset vector (since there isn't one), the NMI vector is
  712. taken over to insure the survival of the key scanner after a RUN/STOP+RESTORE.
  713. A bit of its preamble also had to be copied out of ROM to get at the good
  714. stuff.  If you want a copy of the C-64 listing, you can e-mail me.
  715.  
  716. 6. UUENCODED FILES
  717.  
  718. Here are the binary executables in uuencoded form.  The CRC32s of the four
  719. files are as follows:
  720.  
  721. crc32 = 3398956287 for "keyscan128"
  722. crc32 = 2301926894 for "keyscan64.boot"
  723. crc32 = 1767081474 for "keyscan64"
  724. crc32 = 1604419896 for "keyshow"
  725.  
  726. ================================================================================
  727.  
  728.