bluejay Posted March 8, 2021 Share Posted March 8, 2021 After watching Ben Eater's video on PS/2 keyboards, I now have a general idea on how to work with one. I copied my interface entirely off of Ben's and added it into my computer schematics. For now it only uses the 8 data bits, but I will make use of all the 11 bits in the future. Anyways, PS/2 scan codes seem to have no logic behind them and if it did it would probably be too complicated to come up with a formula to translate the scan codes to ASCII. As of now, I have the computer read the value in the address in which the keyboard resides. It CMPs that value with every PS/2 scan code, then BEQs to the subroutine corresponding to the key pressed. These keyboard input subroutines are divided into multiple segments to avoid out of range errors. This method, as you may think, is highly inefficient in every way. Is there an alternative method to decode PS/2 scan codes? Thoughts and comments on this very basic BIOS I've written in general is appreciated as well. Thanks! 6502bios.bin 6502bios.txt Quote Link to comment https://forums.atariage.com/topic/318103-decoding-ps2-scan-codes/ Share on other sites More sharing options...
emerson Posted March 8, 2021 Share Posted March 8, 2021 Use the scancode as an offset for a lookup table of subroutine addresses. This will allow equal execution time for any input value. Because you will need to left shift the scancodes to calculate the table offset, you will need two tables. One for scancodes 0-127 and the other for scancodes 128-255. I haven't looked at scancodes for a while and forget how high they actually go. You may also want separate table sets for scancodes 1 2 and 3, or have tables that convert undesired scancodes to the desired one. I did that in my PS/2 -> NES converter. I can provide an example code when I get home from work. 1 Quote Link to comment https://forums.atariage.com/topic/318103-decoding-ps2-scan-codes/#findComment-4772550 Share on other sites More sharing options...
emerson Posted March 9, 2021 Share Posted March 9, 2021 (edited) Here are two approaches to the problem. One uses indirect addressing and the other uses the RTS Trick. No guarantees they are %100 correct but this should set you in the right direction. If you only care to support one scancode type, it would be wise to initialize the attached keyboard to use that set of scancodes and ensure the keyboard actually supports it. From my experience scancode 3 was the most common in the dozen or so keyboards I tested (spanning several decades of manufacture dates) but not all keyboards default to scancode 3 on power up. Also, not all keyboards support all scancode sets. Just a thought. Further reading: https://wiki.nesdev.com/w/index.php/RTS_Trick http://www-ug.eecg.toronto.edu/msl/nios_devices/datasheets/PS2 Keyboard Protocol.htm Example Code: Spoiler ;6502 lookup table routines written in asm6 ;-----Method 1----- ; ;- Use indirect addressing and jmp to execute a single subroutine ;- This method is faster but requires 2 bytes of zero page memory ;- Each table can hold a maximum of 128 addresses ;- The routine that is executed must either jmp to another routine or "rts" the "jsr ExecuteSC" call ;- This code assumes all scancodes are only 8 bit values .enum $0000 ;assign zero page variables JumpAddress .dsb 2 ;two byte variable for indirect address scancode .dsb 1 ;one byte variable for your scancode value .ende ExecuteSC: lda scancode ;load current scancode bmi ExecuteSCHigh ;branch to load high scancode routines if scancode >= 0x80 asl ;multiply scancode by 2 to calculate SCTable offset tax ;transfer offset to x lda SCTable+1, y ;load high byte of scancode subroutine address from SCTable sta JumpAddress+1 ;and store it in the high byte of the indirect addressing variable lda SCTable, x ;load low byte of scancode subroutine address from SCTable sta JumpAddress ;and store it in the low byte of the indirect addressing variable jmp (JumpAddress) ;execute scancode subroutine ExecuteSCHigh: sec ;I don't think this is necessary but we'll keep it for now sbc #$80 ;subtract 0x80 from current scancode asl ;multiply difference by 2 to calculate SCTableHigh offset tax ;transfer offset to x lda SCTableHigh+1, x ;load high byte of scancode subroutine address from SCTableHigh sta JumpAddress+1 ;and store it in the high byte of the indirect addressing variable lda SCTableHigh, x ;load low byte of scancode subroutine address from SCTableHigh sta JumpAddress ;and store it in the low byte of the indirect addressing variable jmp (JumpAddress) ;execute scancode subroutine SCTable: .word SC0 ;each .word is a two byte address label ;...more routines here ;stored in little endian format and .word SC127 ;is defined during compilation SCTableHigh: .word SC128 ;...more routines here .word SC255 SC0: ;...do some stuff rts SC127: ;...do some stuff rts SC128 ;...do some stuff rts SC255: ;...do some stuff rts ;-----Method 2----- ; ;- Use the "RTS Trick" by pushing addresses onto the stack as subroutines to be executed ;- This method is slower but saves two bytes of zero page memory ;- All subroutines must end in "rts" ;- Note the minus 1 after the data table addresses ;- This code assumes all scancodes are only 8 bit values .enum $0000 ;assign zero page variables scancode .dsb 1 ;one byte variable for your scancode value .ende ExecuteSC: lda scancode ;load current scancode bmi ExecuteSCHigh ;branch to load high scancode routines if scancode >= 0x80 asl ;multiply scancode by 2 to calculate SCTable offset tax ;transfer offset to x lda SCTable+1, x ;load high byte of scancode subroutine address from SCTable pha ;and push it onto the stack lda SCTable, x ;load low byte of scancode subroutine address from SCTable pha ;and push it onto the stack rts ;execute scancode subroutine ExecuteSCHigh: sec ;I don't think this is necessary but we'll keep it for now sbc #$80 ;subtract 0x80 from current scancode asl ;multiply difference by 2 to calculate SCTableHigh offset tax ;transfer offset to x lda SCTableHigh+1, x ;load high byte of scancode subroutine address from SCTableHigh pha ;and push it onto the stack lda SCTableHigh, x ;load low byte of scancode subroutine address from SCTableHigh pha ;and push it onto the stack rts ;execute scancode subroutine SCTable: .word SC0-1 ;again, note the minus 1 ;...more routines here .word SC127-1 SCTableHigh: .word SC128-1 ;...more routines here .word SC255-1 SC0: ;...do some stuff rts SC127: ;...do some stuff rts SC128 ;...do some stuff rts SC255: ;...do some stuff rts example_6502_lookup_tables.asm Edited March 9, 2021 by emerson 1 Quote Link to comment https://forums.atariage.com/topic/318103-decoding-ps2-scan-codes/#findComment-4772834 Share on other sites More sharing options...
brain Posted March 9, 2021 Share Posted March 9, 2021 I know the following examples are in C, but I believe they will translate to ASM quite well: https://github.com/go4retro/c-key/blob/master/src/poll64.c As others note, use a lookup table for the main scan codes. Many are a 1-1 mapping. The first thing to do in the code is watch for a scan code. Then, for a few compares. E0,E1,F0 and such are special modifiers. Handle them special, and use a state machine to keep things in order. Then, as you get to the main char code, reference into a lookup table. if the result coming back has a high bit clear or something, use as the exact key to send. If the high bit is set, assume the code is an indirect code location reference. strip off the high bit and jump to that code. That way, you can handle special cases. Most keys are of the form (I think scan set 2, but could be 3) <char> and $f0 <char>. Extended chars are $e0 <char> and $e0 $f0 <char> (I might have the $e0 and $f0 backwards, check the code). The only 2 that are truly bizarre are print screen and pause/break. printscreen tries to handle a key down/key up sequence all on the downpress, so it can be handled like the others if desired, but I did it special. Print/Screen is just bizarre and I handled it with the main state machine. You can choose the ignore the $e0 if you want, as the $e0 keys end up factoring back into their reguar versions if you strip it. (1 on keypad is extended version of 1 on main keyboard). Jim 1 Quote Link to comment https://forums.atariage.com/topic/318103-decoding-ps2-scan-codes/#findComment-4773024 Share on other sites More sharing options...
emerson Posted March 9, 2021 Share Posted March 9, 2021 5 hours ago, brain said: Most keys are of the form (I think scan set 2, but could be 3) <char> and $f0 <char> This is correct. I looked back at my notes and every keyboard that I could read defaulted to scancode 2. I had converted everything to scancode 3 in my project which is probably why I thought scancode 3 was most common as I dealt with it most. My apologies. 1 Quote Link to comment https://forums.atariage.com/topic/318103-decoding-ps2-scan-codes/#findComment-4773112 Share on other sites More sharing options...
+DrVenkman Posted March 10, 2021 Share Posted March 10, 2021 You need to read up on @mytek's work on this. Doing this in 6502 has got to be pretty slow. He uses a cheap PIC microcontroller in the TK-II and its variants as used for the 1088XEL, 1088XLD and 576NUC+ projects. https://ataribits.weebly.com/tk-ii.html 1 Quote Link to comment https://forums.atariage.com/topic/318103-decoding-ps2-scan-codes/#findComment-4773745 Share on other sites More sharing options...
bluejay Posted March 10, 2021 Author Share Posted March 10, 2021 Thanks a lot for this tips everyone. I'll look into everything this weekend when I have time. Quote Link to comment https://forums.atariage.com/topic/318103-decoding-ps2-scan-codes/#findComment-4773760 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.