base:scanning_the_keyboard_the_correct_and_non_kernal_way
no way to compare when less than two revisions
Differences
This shows you the differences between two versions of the page.
— | base:scanning_the_keyboard_the_correct_and_non_kernal_way [2015-04-17 04:33] (current) – created - external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Scanning the Keyboard the correct and non-KERNAL way ====== | ||
+ | |||
+ | This routine is for the English Keyboard. | ||
+ | |||
+ | The routine can easilly be extended to handle Control Port #1 input. | ||
+ | |||
+ | |||
+ | ===== The Theory ===== | ||
+ | |||
+ | ==== Limitations ==== | ||
+ | |||
+ | The technical limitation on the C64 Keyboard hardware is that not more than 2 keys may be pressed at the same time if you want to be 100% sure the result is valid. In some cases, three keys will work fine but whenever 3 keys form a right angle in the keyboard scan matrix, a 4th letter will appear. The combination " | ||
+ | |||
+ | In short, the C64 keyboard is not a piano where you can play choords and stuff. | ||
+ | |||
+ | ==== Key Rollover ==== | ||
+ | |||
+ | First, let's see what the definition of the term Key RollOver (KRO) is: | ||
+ | |||
+ | [[http:// | ||
+ | |||
+ | "**A computer keyboard circuit that allows any number of keys to be pressed in succession without having to lift a finger from any of the previous keys.**" | ||
+ | |||
+ | [[http:// | ||
+ | |||
+ | " | ||
+ | |||
+ | [[http:// | ||
+ | |||
+ | "**a facility on an electronic keyboard enabling one or several keystrokes to be registered correctly while another key is depressed.**" | ||
+ | |||
+ | [[http:// | ||
+ | |||
+ | "**A computer keyboard circuit that allows any number of keys to be pressed in succession without having to lift a finger from any of the previous keys.**" | ||
+ | |||
+ | [[http:// | ||
+ | |||
+ | "**A computer keyboard circuit that allows any number of keys to be pressed in succession without having to lift a finger from any of the previous keys. Only a small number of high-end keyboards have n-key rollover. Most have 3-key rollover, which is essential for touch typing.**" | ||
+ | |||
+ | |||
+ | A N-Key RollOver (NKRO) can handle all keys being pressed and registered correctly. The Commodore 64 can only handle 2 keys and is therefore only capable of 2KRO. | ||
+ | |||
+ | For the purpose of this routine is to register keys being entered as correctly as possible having the " | ||
+ | |||
+ | ==== This Keyboard Routine ==== | ||
+ | |||
+ | With the limitations and definitions in mind, this routine will accept up to 3 keys being pressed in succession as long as no shadowing (or ghosting) is produced (ie. a 4th key gets incorrectly registered). | ||
+ | |||
+ | So if you type and hold " | ||
+ | |||
+ | If you type and hold " | ||
+ | |||
+ | What this routine (or any other for that matter) can't do is to correctly report back 2 or 3 keys being pressed so fast that they are registered by the routine to be pressed at the exact same time. In this case, you will need to achieve a "No Activity" | ||
+ | |||
+ | If you need more than 1 Alphanumeric key returned each call, this is not the routine for you. | ||
+ | ==== Speed vs. Memory ==== | ||
+ | |||
+ | The routine presented here favours speed over memory. If memory is tight, you'd wan't to loop some of the checks being carried out. However the code size including tables is roughly 2 pages in size and R-Time is pretty good. | ||
+ | ===== Why not use KERNAL or the other routines on the Codebase? ===== | ||
+ | |||
+ | === KERNAL === | ||
+ | |||
+ | The KERNAL keyscanner is full of bugs. You can try out the following: | ||
+ | |||
+ | * Press and hold " | ||
+ | * Press and hold " | ||
+ | * The infamous Control Port #1 Bug | ||
+ | * Press and hold " | ||
+ | |||
+ | === 3-Key Rollover === | ||
+ | |||
+ | Bruce Craigs 3-Key Rollover routine from C=Hacking #7 takes care of some of these matters but not all. Some bugs remains and some are introduced: | ||
+ | |||
+ | * Press and hold " | ||
+ | * Press and hold " | ||
+ | |||
+ | It's simple to fix though. | ||
+ | |||
+ | === Others === | ||
+ | |||
+ | The other routines by Groepaz and Oswald are for all intents and purposes likely to be of more use to the average programmer than this routine as they are more compact and fast. However they lack some functionality compared to this one and is only good for single key presses (Except Oswalds routine which can handle SHIFT). | ||
+ | ===== Using the routine (API) ===== | ||
+ | |||
+ | just call the routine with a JSR and read the Carry to determine if any valid input is returned. | ||
+ | |||
+ | If the result is valid, the Accumulator will contain the Alphanumeric keys and the X & Y Registers will contain flags for all the non-Alphanumeric keys. | ||
+ | |||
+ | ==== Return ==== | ||
+ | |||
+ | The alphanumeric keys are returned in the Accumulator bit 0-5 (Gives us a total of 64 keys). | ||
+ | All the non-alphanumeric keys get's returned in the X & Y Register. | ||
+ | |||
+ | ^ Result returned in Accumulator (BIT #0 to #5) ^^^^^^^^^^^ | ||
+ | ^ Code ^ Descrition | ||
+ | | $00 | ||
+ | | $01 | ||
+ | | $02 | ||
+ | | $03 | ||
+ | | $04 | ||
+ | | $05 | ||
+ | | $06 | ||
+ | | $07 | ||
+ | | $08 | ||
+ | | $09 | ||
+ | | $0a | ||
+ | | $0b | ||
+ | | $0c | ||
+ | | $0d | ||
+ | | $0e | ||
+ | | $0f | ||
+ | |||
+ | ^ | ||
+ | ^ Bit 7 ^ Bit 6 ^ Bit 5 ^ Bit 4 ^ Bit 3 ^ Bit 2 ^ Bit 1 ^ Bit 0 ^ | ||
+ | | CRSR UD | | ||
+ | |||
+ | ^ | ||
+ | ^ Bit 7 ^ Bit 6 ^ Bit 5 ^ Bit 4 ^ Bit 3 ^ Bit 2 ^ Bit 1 ^ Bit 0 ^ | ||
+ | |RUN STOP | L-SHIFT | | ||
+ | |||
+ | |||
+ | Furthermore, | ||
+ | |||
+ | ^ Condition | ||
+ | |No keyboard activity is detected. | ||
+ | |Control Port #1 Activity is detected. | ||
+ | |Key Shadowing / Ghosting is detected. | ||
+ | |2+ AlphaNum keys is detected within one scan | Set | #$04 | | ||
+ | |Awaiting "No Activity" | ||
+ | |No new Alphanumeric Keys detected (some key(s) being held down AND/OR some Non-Alphanumeric key is causing valid return). | ||
+ | |New Alphanumeric Key returned. Non-Alphanumeric keys may also be returned in X or Y Register | ||
+ | ===== The Routine ===== | ||
+ | |||
+ | The code is pretty well commented but I'll go through some of the theory and thoughts behind the routine in this section. | ||
+ | |||
+ | ==== ZERO Page ==== | ||
+ | |||
+ | Some ZP is used to speed things up a bit. | ||
+ | |||
+ | Simple philosophy: Only variables which changes within a call is used in ZP. Things can happen outside the routine which can mess things up if we expect data in the ZP to stay alive untill next time the routine is called. So Temporary stuff in ZP only! | ||
+ | |||
+ | I will use ZP memory from $50 to $5f. | ||
+ | ==== Data Direction Registers (DDRs) ==== | ||
+ | |||
+ | Since we can forget about KERNAL in this context we have to assume we have no idea of the status of the DDRs of the ports. | ||
+ | |||
+ | (SuperCPU would be nice when setting registers like $dc02 / $dc03 :-D) | ||
+ | ==== Scanning for Activity ==== | ||
+ | |||
+ | We only need to scan the keyboard matrix under certain conditions. First of all, is there any activity at all? secondly, is there any interferance from the Control Port #1? | ||
+ | |||
+ | === Keyboard Activity === | ||
+ | |||
+ | This is simply done by connecting all Keyboard Rows to the Port and check if the result of $dc01 is #$ff (No activity) or <> #$ff (Activity). | ||
+ | |||
+ | === Control Port Activity === | ||
+ | |||
+ | Control Port #1 is connected to the same Port as the Keyboard and will usually mess things up. To detect if there is any activity from the Control Port #1, we disconnect all keyboard row lines to the port and check if there is any activity. | ||
+ | |||
+ | The activity check functions to determine if we should skipp scanning the keyboard if the control port is active. The Keyboard scan routine would quickly have discover it's a non valid input by itself. | ||
+ | |||
+ | Theoretically you could could have a Control Port activation after the scan of the 5th keyboard row which would give you a faulty input (so unlikely that it hurts to think about it). We therefore do a 2nd check after the scan of the keyboard matrix to be 100% sure. | ||
+ | |||
+ | === Scanning the Keyboard Matrix === | ||
+ | |||
+ | In my point of view, reading the keyboard matrix is like taking a picture of a situation. How clear your picture is depends on how fast the lens can close to capture the picture. Therefore, I wan't to scan the keyboard matrix as fast as possible to get a accurate presentation of the keyboard scan matrix. | ||
+ | |||
+ | < | ||
+ | // | ||
+ | // Scan Keyboard Matrix | ||
+ | |||
+ | lda #%11111110 | ||
+ | sta $dc00 | ||
+ | ldy $dc01 | ||
+ | sty ScanResult+7 | ||
+ | sec | ||
+ | rol | ||
+ | sta $dc00 | ||
+ | ldy $dc01 | ||
+ | sty ScanResult+6 | ||
+ | rol | ||
+ | sta $dc00 | ||
+ | ldy $dc01 | ||
+ | sty ScanResult+5 | ||
+ | rol | ||
+ | sta $dc00 | ||
+ | ldy $dc01 | ||
+ | sty ScanResult+4 | ||
+ | rol | ||
+ | sta $dc00 | ||
+ | ldy $dc01 | ||
+ | sty ScanResult+3 | ||
+ | rol | ||
+ | sta $dc00 | ||
+ | ldy $dc01 | ||
+ | sty ScanResult+2 | ||
+ | rol | ||
+ | sta $dc00 | ||
+ | ldy $dc01 | ||
+ | sty ScanResult+1 | ||
+ | rol | ||
+ | sta $dc00 | ||
+ | ldy $dc01 | ||
+ | sty ScanResult | ||
+ | </ | ||
+ | |||
+ | You can ofcourse choose to use a loop if memory is your concern. | ||
+ | |||
+ | As you can see, I have chosen not to " | ||
+ | |||
+ | |||
+ | === Scanning for non-Alphanumeric Keys === | ||
+ | |||
+ | This is easilly done by doing some arithmetics on the Scan Result. | ||
+ | |||
+ | For the result returned in X, you simply need to read the 8th line of the ScanResult as it's already ligned up perfectly for us. | ||
+ | |||
+ | For the return in the Y-Register, some arithmic trickery is needed (check source code). | ||
+ | |||
+ | A couple of ZP Temporary storage is used which then simply is ready into the X & Y-Register upon exit of the routine. | ||
+ | |||
+ | |||
+ | === Deciphering the Keyboard Scan Matrix === | ||
+ | |||
+ | I unrolled this part of the Routine as the looped version suxored R-Time. It works as follows: | ||
+ | |||
+ | - Is there any keys pressed in the current row? -> NO: Check Next Row | ||
+ | - YES: Use X-Register as a pointer index into a key-table and arithmically shift the values in the scan untill you find the keys which are activated | ||
+ | - Once a key as found, check for overflow (max 3 keys pr. scan is allowed due to keyboard bugs) | ||
+ | - Store key in BufferNew | ||
+ | |||
+ | Would definately be handy with an extra register here :-O | ||
+ | |||
+ | |||
+ | === Buffers === | ||
+ | |||
+ | This is where the magic happens. The BufferNew is compared agains BufferOld to check if there are any new keys present (This takes care of the DeBounce issue). If there is, they get added to Buffer which is the output buffer (holding up to 3 keys as well^^) which then in turn, cronologically returns the keys to the caller. It's kinda hard to wrap ones head about dealing with several buffers and such. For me, trial and error combined with a good test program helped a lot when coding this. | ||
+ | ===== Known Issues ===== | ||
+ | |||
+ | At the moment, none that I am aware of. | ||
+ | |||
+ | ===== The Code ===== | ||
+ | |||
+ | In good old KickAss Format. | ||
+ | |||
+ | < | ||
+ | / | ||
+ | Keyboard IO Routine | ||
+ | ~~~~~~~~~~~~~~~~~~~ | ||
+ | By: TWW/CTR | ||
+ | |||
+ | |||
+ | Preparatory Settings | ||
+ | ~~~~~~~~~~~~~~~~~~~~ | ||
+ | None | ||
+ | |||
+ | |||
+ | Destroys | ||
+ | ~~~~~~~~ | ||
+ | Accumulator | ||
+ | X-Register | ||
+ | Y-Register | ||
+ | Carry / Zero / Negative | ||
+ | $dc00 | ||
+ | $dc01 | ||
+ | $50-$5f | ||
+ | |||
+ | |||
+ | Footprint | ||
+ | ~~~~~~~~~ | ||
+ | #$206 Bytes | ||
+ | |||
+ | |||
+ | Information | ||
+ | ~~~~~~~~~~~ | ||
+ | The routine uses "2 Key rollower" | ||
+ | If 2 or 3 keys are pressed simultaneously (within 1 scan) a "No Activity" | ||
+ | RESTORE is not detectable and must be handled by NMI IRQ. | ||
+ | SHIFT LOCK is not detected due to unreliability. | ||
+ | |||
+ | |||
+ | Usage | ||
+ | ~~~~~ | ||
+ | Example Code: | ||
+ | |||
+ | jsr Keyboard | ||
+ | bcs NoValidInput | ||
+ | stx TempX | ||
+ | sty TempY | ||
+ | cmp #$ff | ||
+ | beq NoNewAphanumericKey | ||
+ | // Check A for Alphanumeric keys | ||
+ | sta $0400 | ||
+ | NoNewAphanumericKey: | ||
+ | // Check X & Y for Non-Alphanumeric Keys | ||
+ | ldx TempX | ||
+ | ldy TempY | ||
+ | stx $0401 | ||
+ | sty $0402 | ||
+ | | ||
+ | |||
+ | |||
+ | Returned | ||
+ | ~~~~~~~~ | ||
+ | |||
+ | +=================================================+ | ||
+ | | | ||
+ | +===========+===========+=============+===========+ | ||
+ | | $00 - @ | $10 - p | $20 - SPC | $30 - 0 | | ||
+ | | $01 - a | $11 - q | $21 - | $31 - 1 | | ||
+ | | $02 - b | $12 - r | $22 - | $32 - 2 | | ||
+ | | $03 - c | $13 - s | $23 - | $33 - 3 | | ||
+ | | $04 - d | $14 - t | $24 - | $34 - 4 | | ||
+ | | $05 - e | $15 - u | $25 - | $35 - 5 | | ||
+ | | $06 - f | $16 - v | $26 - | $36 - 6 | | ||
+ | | $07 - g | $17 - w | $27 - | $37 - 7 | | ||
+ | | $08 - h | $18 - x | $28 - | $38 - 8 | | ||
+ | | $09 - i | $19 - y | $29 - | $39 - 9 | | ||
+ | | $0a - j | $1a - z | $2a - * | $3a - : | | ||
+ | | $0b - k | $1b - | $2b - + | $3b - ; | | ||
+ | | $0c - l | $1c - £ | $2c - , | $3c - | | ||
+ | | $0d - m | $1d - | $2d - - | $3d - = | | ||
+ | | $0e - n | $1e - ^ | $2e - . | $3e - | | ||
+ | | $0f - o | $1f - <- | $2f - / | $3f - | | ||
+ | +-----------+-----------+-------------+-----------+ | ||
+ | |||
+ | +================================================================================ | ||
+ | | | ||
+ | +=========+=========+=========+=========+=========+=========+=========+=========+ | ||
+ | | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | | ||
+ | +---------+---------+---------+---------+---------+---------+---------+---------+ | ||
+ | | CRSR UD | | ||
+ | +---------+---------+---------+---------+---------+---------+---------+---------+ | ||
+ | |||
+ | +================================================================================ | ||
+ | | | ||
+ | +=========+=========+=========+=========+=========+=========+=========+=========+ | ||
+ | | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | | ||
+ | +---------+---------+---------+---------+---------+---------+---------+---------+ | ||
+ | |RUN STOP | L-SHIFT | | ||
+ | +---------+---------+---------+---------+---------+---------+---------+---------+ | ||
+ | |||
+ | CARRY: | ||
+ | - Set = Error Condition (Check A for code): | ||
+ | A = #$01 => No keyboard activity is detected. | ||
+ | A = #$02 => Control Port #1 Activity is detected. | ||
+ | A = #$03 => Key Shadowing / Ghosting is detected. | ||
+ | A = #$04 => 2 or 3 new keys is detected within one scan | ||
+ | A = #$05 => Awaiting "No Activity" | ||
+ | - Clear = Valid input | ||
+ | A = #$ff => No new Alphanumeric Keys detected (some key(s) being held down AND/OR some Non-Alphanumeric key is causing valid return). | ||
+ | A <> #$ff => New Alphanumeric Key returned. Non-Alphanumeric keys may also be returned in X or Y Register | ||
+ | |||
+ | Issues/ | ||
+ | ~~~~~~~~~~~~ | ||
+ | - None | ||
+ | |||
+ | |||
+ | Improvements: | ||
+ | ~~~~~~~~~~~~~ | ||
+ | - Replace the subroutine with a pseudocommand and account for speedcode parameter (Memory vs. Cycles). | ||
+ | - Shorten the routine / Optimize if possible. | ||
+ | |||
+ | |||
+ | History: | ||
+ | ~~~~~~~~ | ||
+ | V2.5 - New test tool. | ||
+ | Added return of error codes. | ||
+ | Fixed a bug causing Buffer Overflow. | ||
+ | Fixed a bug in Non Alphanumerical Flags from 2.0. | ||
+ | V2.1 - Shortened the source by adding .for loops & Updated the header and some comments. | ||
+ | Added " | ||
+ | V2.0 - Added return of non-Alphanumeric keys into X & Y-Registers. | ||
+ | Small optimizations here and there. | ||
+ | V1.1 - Unrolled code to make it faster and optimized other parts of it. | ||
+ | | ||
+ | V1.0 - First Working Version along with test tool. | ||
+ | |||
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | ||
+ | |||
+ | .pc = * " | ||
+ | |||
+ | |||
+ | // ZERO PAGE Varibles | ||
+ | .const ScanResult | ||
+ | .const BufferNew | ||
+ | .const KeyQuantity | ||
+ | .const NonAlphaFlagX | ||
+ | .const NonAlphaFlagY | ||
+ | .const TempZP | ||
+ | .const SimultaneousKeys = $5f // 1 byte | ||
+ | |||
+ | // Operational Variables | ||
+ | .var MaxKeyRollover = 3 | ||
+ | |||
+ | Keyboard: | ||
+ | { | ||
+ | jmp Main | ||
+ | |||
+ | |||
+ | // | ||
+ | // Routine for Scanning a Matrix Row | ||
+ | |||
+ | KeyInRow: | ||
+ | asl | ||
+ | bcs *+5 | ||
+ | jsr KeyFound | ||
+ | .for (var i = 0 ; i < 7 ; i++) { | ||
+ | inx | ||
+ | asl | ||
+ | bcs *+5 | ||
+ | jsr KeyFound | ||
+ | } | ||
+ | rts | ||
+ | |||
+ | |||
+ | // | ||
+ | // Routine for handling: Key Found | ||
+ | |||
+ | KeyFound: | ||
+ | stx TempZP | ||
+ | dec KeyQuantity | ||
+ | bmi OverFlow | ||
+ | ldy KeyTable,x | ||
+ | ldx KeyQuantity | ||
+ | sty BufferNew,x | ||
+ | ldx TempZP | ||
+ | rts | ||
+ | |||
+ | // | ||
+ | // Routine for handling: Overflow | ||
+ | |||
+ | OverFlow: | ||
+ | pla // Dirty hack to handle 2 layers of JSR | ||
+ | pla | ||
+ | pla | ||
+ | pla | ||
+ | // Don't manipulate last legal buffer as the routine will fix itself once it gets valid input again. | ||
+ | lda #$03 | ||
+ | sec | ||
+ | rts | ||
+ | |||
+ | |||
+ | // | ||
+ | // Exit Routine for: No Activity | ||
+ | |||
+ | NoActivityDetected: | ||
+ | // Exit With A = #$01, Carry Set & Reset BufferOld. | ||
+ | lda #$00 | ||
+ | sta SimultaneousAlphanumericKeysFlag | ||
+ | stx BufferOld | ||
+ | stx BufferOld+1 | ||
+ | stx BufferOld+2 | ||
+ | sec | ||
+ | lda #$01 | ||
+ | rts | ||
+ | |||
+ | |||
+ | // | ||
+ | // Exit Routine for Control Port Activity | ||
+ | |||
+ | ControlPort: | ||
+ | // Exit with A = #$02, Carry Set. Keep BufferOld to verify input after Control Port activity ceases | ||
+ | sec | ||
+ | lda #$02 | ||
+ | rts | ||
+ | |||
+ | |||
+ | // | ||
+ | // Configure Data Direction Registers | ||
+ | Main: | ||
+ | ldx #$ff | ||
+ | stx $dc02 // Port A - Output | ||
+ | ldy #$00 | ||
+ | sty $dc03 // Port B - Input | ||
+ | |||
+ | |||
+ | // | ||
+ | // Check for Port Activity | ||
+ | |||
+ | sty $dc00 // Connect all Keyboard Rows | ||
+ | cpx $dc01 | ||
+ | beq NoActivityDetected | ||
+ | |||
+ | lda SimultaneousAlphanumericKeysFlag | ||
+ | beq !+ | ||
+ | // Waiting for all keys to be released before accepting new input. | ||
+ | lda #$05 | ||
+ | sec | ||
+ | rts | ||
+ | !: | ||
+ | |||
+ | // | ||
+ | // Check for Control Port #1 Activity | ||
+ | |||
+ | stx $dc00 // Disconnect all Keyboard Rows | ||
+ | cpx $dc01 // Only Control Port activity will be detected | ||
+ | bne ControlPort | ||
+ | |||
+ | |||
+ | // | ||
+ | // Scan Keyboard Matrix | ||
+ | |||
+ | lda #%11111110 | ||
+ | sta $dc00 | ||
+ | ldy $dc01 | ||
+ | sty ScanResult+7 | ||
+ | sec | ||
+ | .for (var i = 6 ; i > -1 ; i--) { | ||
+ | rol | ||
+ | sta $dc00 | ||
+ | ldy $dc01 | ||
+ | sty ScanResult+i | ||
+ | } | ||
+ | |||
+ | |||
+ | // | ||
+ | // Check for Control Port #1 Activity (again) | ||
+ | |||
+ | stx $dc00 // Disconnect all Keyboard Rows | ||
+ | cpx $dc01 // Only Control Port activity will be detected | ||
+ | bne ControlPort | ||
+ | |||
+ | |||
+ | // | ||
+ | // Initialize Buffer, Flags and Max Keys | ||
+ | |||
+ | // Reset current read buffer | ||
+ | stx BufferNew | ||
+ | stx BufferNew+1 | ||
+ | stx BufferNew+2 | ||
+ | |||
+ | // Reset Non-AlphaNumeric Flag | ||
+ | inx | ||
+ | stx NonAlphaFlagY | ||
+ | |||
+ | // Set max keys allowed before ignoring result | ||
+ | lda # | ||
+ | sta KeyQuantity | ||
+ | |||
+ | // Counter to check for simultaneous alphanumeric key-presses | ||
+ | lda #$fe | ||
+ | sta SimultaneousKeys | ||
+ | |||
+ | |||
+ | // | ||
+ | // Check and flag Non Alphanumeric Keys | ||
+ | |||
+ | lda ScanResult+6 | ||
+ | eor #$ff | ||
+ | and # | ||
+ | lsr | ||
+ | sta NonAlphaFlagY | ||
+ | lda ScanResult+0 | ||
+ | eor #$ff | ||
+ | and # | ||
+ | ora NonAlphaFlagY | ||
+ | sta NonAlphaFlagY | ||
+ | lda ScanResult+1 | ||
+ | eor #$ff | ||
+ | and # | ||
+ | ora NonAlphaFlagY | ||
+ | sta NonAlphaFlagY | ||
+ | |||
+ | lda ScanResult+7 | ||
+ | eor #$ff | ||
+ | sta NonAlphaFlagX | ||
+ | |||
+ | |||
+ | // | ||
+ | // Check for pressed key(s) | ||
+ | |||
+ | lda ScanResult+7 | ||
+ | cmp #$ff | ||
+ | beq *+5 | ||
+ | jsr KeyInRow | ||
+ | .for (var i = 6 ; i > -1 ; i--) { | ||
+ | ldx #[7-i]*8 | ||
+ | lda ScanResult+i | ||
+ | beq *+5 | ||
+ | jsr KeyInRow | ||
+ | } | ||
+ | |||
+ | |||
+ | // | ||
+ | // Key Scan Completed | ||
+ | |||
+ | // Put any new key (not in old scan) into buffer | ||
+ | ldx # | ||
+ | !: lda BufferNew,x | ||
+ | cmp #$ff | ||
+ | beq Exist // Handle ' | ||
+ | cmp BufferOld | ||
+ | beq Exist | ||
+ | cmp BufferOld+1 | ||
+ | beq Exist | ||
+ | cmp BufferOld+2 | ||
+ | beq Exist | ||
+ | // New Key Detected | ||
+ | inc BufferQuantity | ||
+ | ldy BufferQuantity | ||
+ | sta Buffer,y | ||
+ | // Keep track of how many new Alphanumeric keys are detected | ||
+ | inc SimultaneousKeys | ||
+ | beq TooManyNewKeys | ||
+ | Exist: | ||
+ | dex | ||
+ | bpl !- | ||
+ | |||
+ | // Anything in Buffer? | ||
+ | ldy BufferQuantity | ||
+ | bmi BufferEmpty | ||
+ | // Yes: Then return it and tidy up the buffer | ||
+ | dec BufferQuantity | ||
+ | lda Buffer | ||
+ | ldx Buffer+1 | ||
+ | stx Buffer | ||
+ | ldx Buffer+2 | ||
+ | stx Buffer+1 | ||
+ | jmp Return | ||
+ | |||
+ | BufferEmpty: | ||
+ | lda #$ff | ||
+ | |||
+ | Return: | ||
+ | clc | ||
+ | // Copy BufferNew to BufferOld | ||
+ | ldx BufferNew | ||
+ | stx BufferOld | ||
+ | ldx BufferNew+1 | ||
+ | stx BufferOld+1 | ||
+ | ldx BufferNew+2 | ||
+ | stx BufferOld+2 | ||
+ | // Handle Non Alphanumeric Keys | ||
+ | ldx NonAlphaFlagX | ||
+ | ldy NonAlphaFlagY | ||
+ | rts | ||
+ | |||
+ | TooManyNewKeys: | ||
+ | sec | ||
+ | lda #$ff | ||
+ | sta BufferQuantity | ||
+ | sta SimultaneousAlphanumericKeysFlag | ||
+ | lda #$04 | ||
+ | rts | ||
+ | |||
+ | // | ||
+ | KeyTable: | ||
+ | .byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff // CRSR DOWN, F5, F3, F1, F7, CRSR RIGHT, RETURN, INST DEL | ||
+ | .byte $ff, $05, $13, $1a, $34, $01, $17, $33 // LEFT SHIFT, " | ||
+ | .byte $18, $14, $06, $03, $36, $04, $12, $35 // " | ||
+ | .byte $16, $15, $08, $02, $38, $07, $19, $37 // " | ||
+ | .byte $0e, $0f, $0b, $0d, $30, $0a, $09, $39 // " | ||
+ | .byte $2c, $00, $3a, $2e, $2d, $0c, $10, $2b // ",", | ||
+ | .byte $2f, $1e, $3d, $ff, $ff, $3b, $2a, $1c // "/", | ||
+ | .byte $ff, $11, $ff, $20, $32, $ff, $1f, $31 // RUN STOP, " | ||
+ | |||
+ | BufferOld: | ||
+ | .byte $ff, $ff, $ff | ||
+ | |||
+ | Buffer: | ||
+ | .byte $ff, $ff, $ff, $ff | ||
+ | |||
+ | BufferQuantity: | ||
+ | .byte $ff | ||
+ | |||
+ | SimultaneousAlphanumericKeysFlag: | ||
+ | .byte $00 | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | That's it! | ||
+ | |||
+ | |||
+ | The test program: [[http:// | ||
+ | |||
base/scanning_the_keyboard_the_correct_and_non_kernal_way.txt · Last modified: 2015-04-17 04:33 by 127.0.0.1