6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun May 05, 2024 5:19 pm

All times are UTC




Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Mon Apr 25, 2022 12:25 pm 
Offline
User avatar

Joined: Sat Jul 24, 2021 1:37 pm
Posts: 282
Hello everyone,

I've been working on a keyboard driver for my kernel. I'm sure many others here have done the same, and I wonder if you see any improvement I could make to my design.

I want to provide two hooks to user programs:
- one will pass "raw" scancodes, as defined by Scancodes Set 2. I will only call this for key presses, not key releases (as I don't see a use for it?), and send a single byte as an argument containing the scancode. I will need to re-map escaped scancodes (things like modifier keys, pause, etc) to be a single byte, but there is plenty of space left in the Set 2 encoding to do so.
- the second hook will pass parsed characters, so a program does not have to manage the keyboard state machine. I plan on using CP1252 encoding for this, as it contains all the characters I need for my locale and is a single byte. I will re-map values below 0x20 to represent non-printable key presses (such as pressing Escape, F1-12, Home, etc). I might need to pass a second byte for modifier state (is Ctrl pressed...).

The kernel maintains a few flags to represent whether modifier keys are being pressed, or if dead keys have been activated. Mapping between scancodes and final encoding is done via look up tables. One issue I have is that is not very space efficient, as I need to maintain look up tables for every possible state of the keyboard. For example, I need one for when the diaeresis key (¨) has been pressed beforehand, same for circumflex (^) and a couple other dead keys, as well as Shift and AltGr. However, there are only a handful of characters in the dead keys LUTs, so they are mostly empty but each still take 256 bytes of ROM space...

Finally, for display I currently use a 16x2 LCD, and I maintain another LUT, mapping between CP1252 to the font available on the built-in controller. Not all characters are available though, and so in the future I plan to use the character RAM to add some custom ones.

Do you see anything I could improve in this scheme? Something I'm missing that I'll need down the line to build useful programs? Let me know :wink:

_________________
BB816 Computer YouTube series


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 25, 2022 1:27 pm 
Offline
User avatar

Joined: Sun Nov 07, 2021 4:11 pm
Posts: 101
Location: Toronto, Canada
akohlbecker wrote:
The kernel maintains a few flags to represent whether modifier keys are being pressed, or if dead keys have been activated. Mapping between scancodes and final encoding is done via look up tables. One issue I have is that is not very space efficient, as I need to maintain look up tables for every possible state of the keyboard. For example, I need one for when the diaeresis key (¨) has been pressed beforehand, same for circumflex (^) and a couple other dead keys, as well as Shift and AltGr. However, there are only a handful of characters in the dead keys LUTs, so they are mostly empty but each still take 256 bytes of ROM space...


Do you really need to use a LUT for modifier keys? I presume that, for most of these, only a handful of keypresses are going to generate valid events, and so a map might be more space efficient at a relatively small cost in terms of time complexity, even if you implement it naïvely.

Also, I would suggest at least having the ability to emit an event on key release… in my experience, they tend to be more useful than one would think, for example for handling key repeats.


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 25, 2022 2:54 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1928
Location: Sacramento, CA, USA
akohlbecker wrote:
... I will only call this for key presses, not key releases (as I don't see a use for it?), and send a single byte as an argument containing the scancode. I will need to re-map escaped scancodes (things like modifier keys, pause, etc) to be a single byte, but there is plenty of space left in the Set 2 encoding to do so ...

Some games would benefit substantially from knowing the moment a key is released.

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 27, 2022 7:28 am 
Offline

Joined: Tue Apr 20, 2010 4:02 pm
Posts: 31
I have been toying a little with encodings and keyboard layouts etc.
I tried to develop a generic solution (supporting multiple languages etc.) so this may be overkill for your use case...

1. Low level hardware reading routine returns two bytes (one byte keycode, one byte shift states). I do some basic remapping of the hardware key codes to ASCII. The ascii variants try to depict the symbol drawn on the keyboard. This way, the programmer using the routine does not have to remember or use special codes.

2. KeyMap can be used to convert keystrokes to actual characters.

The routine for keymap is as follows:

Code:
;Input maps
;==========
;
;Input maps convert raw input from keyboard to ascii characters.
;Different keyboard maps for different languages may be defined.
;
;Key maps support:
;
;  shifts
;  dead keys
;
;Input map is basically used when writing text into text editor.
;Therefore it does not have to be too fast (it will be called only when user pressed the key).
;
;Input map format
;----------------
;
;Input map defines one or more layouts.
;Layout is used based on the state of shift keys, lock keys and dead keys.
;
;shift - changes the input map when pressed (does not change when released)
;lock  - permanently changes the layout, until pressed again (or some other key changes the state)
;dead  - changes the state until next key pressed
;
;Up to 8 shift, lock and dead keys are supported.
;
;Common states are SHIFT, CAPS_LOCK.
;We support 4 lock keys and 4 dead keys.

LOCK_STATE = 200    ;lock state
KEY_MAP_PTR = 198;199

DEAD_KEY_MASK = %00111111

;Keyboard layout format
;----------------------
;shift_state:   byte
;lock_state:    byte
;number_of_ranges: byte
;    first,last:byte
;    chars:byte [last-first+1]
;There is an exception -> when the first code is 0, the next code defines byte, that will be xored with the key to produce the code

;Range definition
;
;<first_key>, <count>, <array>

;Key definition
;key - if key is 0, tuple from-to follows
;char - if there is tuple - array of replacements follows
;       if the first byte in array is 0, one byte that is XORed with the key follows (it can even be 0, meaning the key remains same)

;Alt:
;       if the key was not found, original key is returned?

MAP_KEY_START

InitKeyMap .PROC
      sta KEY_MAP_PTR
      stx KEY_MAP_PTR+1
      rts
.ENDP

MapKey  .PROC
;Purpose:
;   Translate key press to character.
;Input:
;   A    key
;   X    shift state

layout = aux   ;aux2
key    = aux3
shift  = aux4
layout_len = aux6 

      sta key
      stx shift
      mwa KEY_MAP_PTR layout

   ;find appropriate layout based on shift state and lock state
find_layout
      ldy #1
      lda (layout),y
      cmp shift
      bne nextl
      iny
      lda (layout),y
      cmp LOCK_STATE
      beq layout_found
nextl
      jsr NextLayout
      bne find_layout

      ;No rule for this key was found, return the original key and shift state,
      ;so it can be reused by caller.
no_char
      lda key
      ldx shift
      clc
      rts

NextLayout
      ldy #0
      lda (layout),y
      beq lastl
      clc
      adc layout
      sta layout
      scc
      inc layout+1
lastl   rts

;We didn't find the key in the curren layout, but another layout can be chained.

key_not_found
      ldy #3
      lda (layout),y
      beq no_char
      tax
      mwa KEY_MAP_PTR layout
      dex
      beq layout_found
@      jsr NextLayout
      dex
      bne @-

layout_found
      ldy #0
      lda (layout),y
      sta layout_len

      ldy #4      ;first sequence
.def :key_test
      cpy layout_len      ;If this is end of the layout, we didn't find the key
      beq key_not_found
      lda (layout),y      ;range length
      beq dead_key
      iny

      sta aux5         ;range length
      lda key
      sec
      sbc (layout),y      ;first key
      iny
      bcc skip_range
      cmp aux5         ;range length
      bcs skip_range      
      sta shift      ;reuse
      lda (layout),y      ;first char
      bne array_range
eor_range      
      iny
      lda key
      eor (layout),y
      bcc key_found      ;jump always
array_range
      tya
      clc
      adc shift      ;reuse
      tay
      lda (layout),y
      bcc key_found

skip_range
      lda (layout),y      ;first char
      beq skip2      ;if range starts with 0, skip 2 bytes
      tya            ;otherwise skip range size
      clc
      adc aux5
      tay
      bne key_test

skip2   iny
skip1   iny
      bne key_test

dead_key  ;0, key, lock_state
      iny
      lda (layout),y      ;key
      iny
      cmp key
      bne skip1         ;not the same key
      lda lock_state
      and #DEAD_KEY_MASK   ;dead key bits are always set while lock bits are always switched 
      eor (layout),y
      sta lock_state
      lda #0
      clc               ;key was consumed
      rts

key_found
      sec
key_done
      pha
      lda lock_state      ;clear dead key state
      and #DEAD_KEY_MASK
      sta lock_state
      pla
      rts
.ENDP

.print  "Key mapping:", * - MAP_KEY_START


Input map can look like this:

Code:
;Czech keyboard for TCECHO encoding.
;TODO: Maybe we could use accute command to modify the char of keyboard.

KEYBOARD_CZECH  .PROC

DEAD_CARON  = 128
DEAD_ACUTE  = 64

normal
   .byte normal_end-*
   .byte 0,0
   .byte 0               ;no base
    .byte 26, 'A', 0, 32   ;this will make the basic key lowercase
   .byte 10, '0', $17, $2b, $05, $13, $03, $12, $1a, $19, $01, $09
   .byte 1, '-', $15   ;ú
   .byte 1, ';', $0a   ;ů
   .byte 0, '<' ,DEAD_ACUTE
   .byte 0, KEY_CAPS_LOCK, $01
   .byte 31, 32, 0, 0       ;other keys are used without change (otherwise they are reported as unused)
normal_end

cps_lock
   .byte cps_lock_end-*
   .byte 0, 1            ;CAPS LOCK
   .byte 1
   .byte 26, 'A', 0, 0
   .byte 1, '-', $08   ;Ú
   .byte 1, ';', $0b   ;Ů
   .byte 10, '0', $07, $2b, $06, $1d, $16, $1c, $18, $1f, $11, $0c
cps_lock_end

caron
   .byte caron_end-*
   .byte 0, DEAD_CARON
   .byte 1
   ;             C    D    E
   .byte 3, 'C', $03, $04, $05
   ;             R    S    T    U
   .byte 4, 'R', $12, $13, $14, $0a
   .byte 1, 'N', $0e
   .byte 1, 'Z', $1a   
caron_end

shift_caron
   .byte shift_caron_end-*
   .byte KEY_SHIFT, DEAD_CARON
   .byte 1
   ;             C    D    E
   .byte 3, 'C', $16, $02, $06
   ;             R    S    T    U
   .byte 4, 'R', $1c, $1d, $1e, $0b
   .byte 1, 'N', $0d
   .byte 1, 'Z', $18
shift_caron_end

acute
   .byte accute_end-*
   .byte 0, DEAD_ACUTE
   .byte 1
   .byte 1, 'A', $01
   .byte 1, 'E', $17
   .byte 1, 'I', $09
   .byte 1, 'O', $0f
   .byte 1, 'U', $15
   .byte 1, 'Y', $19
      
accute_end

shift_acute
   .byte shift_accute_end-*
   .byte KEY_SHIFT, DEAD_ACUTE
   .byte 1
   .byte 1, 'A', $11
   .byte 1, 'E', $07
   .byte 1, 'I', $0c
   .byte 1, 'O', $10
   .byte 1, 'U', $08
   .byte 1, 'Y', $1f
      
shift_accute_end

shift
   .byte shift_end - *
   .byte KEY_SHIFT, 0            ;SHIFT
   .byte 2
   .byte 10, '0', 0, 0
   .byte 0, '<' ,DEAD_CARON
shift_end

;   .byte '='-'*'+1, '*'
      ;      *+,-./01   2    3456789:;<=
;      .byte '^\[_]?)!', 34, '#$%&/@(::<|'

shift_caps_lock
   .byte shift_caps_lock_end - *
   .byte KEY_SHIFT, 1            ;SHIFT + CAPS_LOCK
   .byte 3               ;use shift as base keyboard
   .byte 26, 'A', 0, 32   ;letters are small again, otherwise this is normal shift keyboard
shift_caps_lock_end

end
   .byte 0   

.print "Czech key map:", * - KEYBOARD_ENGLISH

   .ENDP



Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 27, 2022 7:33 am 
Offline

Joined: Tue Apr 20, 2010 4:02 pm
Posts: 31
Another option which I am exploring now is supporting different encodings (meaning i have a table, that allows me to do uppercase, lowercase, combining diacritic marks with letters).

As I have a VM in my OS, KayMap can be just a routine in VM (which can call uppercase, and diacritic combine to avoid use of the tables in KayMap for this).


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 27, 2022 8:57 pm 
Offline
User avatar

Joined: Sat Jul 24, 2021 1:37 pm
Posts: 282
CountChocula wrote:
Do you really need to use a LUT for modifier keys? I presume that, for most of these, only a handful of keypresses are going to generate valid events, and so a map might be more space efficient at a relatively small cost in terms of time complexity, even if you implement it naïvely.


I need to brush up on my data structures... :-) I've been experimenting with a map for the scancode translation and below is what I have. What do you think?

Code:
.regular_2_to_3: ; sequence of pairs of bytes, even bytes represent a Set 2 scancode, odd bytes represent the equivalent Set 3 scancode. Null terminated
    !byte $7c ; Ctrl+PrintScr, Shift+PrintScr
    !byte $57

    ...

    !byte $00

; translate_regular_2_to_3: translate any non-escaped scancode from set 2 to set 3
; argument A=scancode
; returns A=translated scancode
translate_regular_2_to_3:
    ldx # $0 ; start at 0
-   cmp .regular_2_to_3, x      ; compare scancode with key at index X
    beq +                       ; if equal, we found a translation
    inx                         ; otherwise increment index twice to move to next item
    inx
    ldy .regular_2_to_3, x      ; check to see if that new location is zero
    beq ++                      ; if it is, we reached the end of the map, return from subroutine. A hasn't been modified
    jmp -                       ; if it is not, go to the start of the loop again
+   lda .regular_2_to_3 + 1, x  ; if we found a translation, load the new value
++  rts



CountChocula wrote:
Also, I would suggest at least having the ability to emit an event on key release… in my experience, they tend to be more useful than one would think, for example for handling key repeats.


barrym95838 wrote:
Some games would benefit substantially from knowing the moment a key is released.


That makes sense. I've been rethinking my plans a bit: for the scancodes handling, my kernel will translate from Set 2 to Set 3 (no sense in inventing my own encoding when a regular, single byte encoding already exists). Then there will be a key pressed and a key released event. For things like function keys, I had thought about using the space below $20 in cp1252 to pass them, but programs could well listen to both the key pressed and the character pressed events and act according to what they support.

rudla.kudla wrote:
I have been toying a little with encodings and keyboard layouts etc.
I tried to develop a generic solution (supporting multiple languages etc.) so this may be overkill for your use case...

1. Low level hardware reading routine returns two bytes (one byte keycode, one byte shift states). I do some basic remapping of the hardware key codes to ASCII. The ascii variants try to depict the symbol drawn on the keyboard. This way, the programmer using the routine does not have to remember or use special codes.

2. KeyMap can be used to convert keystrokes to actual characters.

The routine for keymap is as follows:


This is very cool stuff! Interesting way to define the layout in assembler in a generic fashion. So far I'm going the route of a pre-processing step (script reads layout information and spits out binary files I can include), but I'll keep this approach in mind.

_________________
BB816 Computer YouTube series


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 27, 2022 9:02 pm 
Offline
User avatar

Joined: Sat Jul 24, 2021 1:37 pm
Posts: 282
During my research I found this IBM document which I found really useful. It talks about how the PS/2 protocol works, scancodes and their oddities, and how the BIOS on IBM PCs report scancodes to programs. It is well worth a read


Attachments:
ibm_hitrc11.pdf [1.1 MiB]
Downloaded 25 times

_________________
BB816 Computer YouTube series
Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 27, 2022 11:46 pm 
Offline
User avatar

Joined: Sun Nov 07, 2021 4:11 pm
Posts: 101
Location: Toronto, Canada
akohlbecker wrote:
I need to brush up on my data structures... :-) I've been experimenting with a map for the scancode translation and below is what I have. What do you think?


I think it's fine for such a simple use case. You could save one byte and two cycles by separating out the keys from the values—e.g.:

Code:
.regular_2_to_3_keys: ;
    !byte $7c ; Ctrl+PrintScr, Shift+PrintScr

    ...

    !byte $00

.regular_2_to_3_values:
    !byte $57 ; Ctrl+PrintScr, Shift+PrintScr

    ... etc. etc., one byte for each key; no need for a zero terminator.

; translate_regular_2_to_3: translate any non-escaped scancode from set 2 to set 3
; argument A=scancode
; returns A=translated scancode
translate_regular_2_to_3:
    ldx # $0 ; start at 0
-   cmp .regular_2_to_3_keys, x      ; compare scancode with key at index X
    beq +                       ; if equal, we found a translation
    inx                         ; otherwise increment index to move to next item
    ldy .regular_2_to_3_keys, x      ; check to see if that new location is zero
    beq ++                      ; if it is, we reached the end of the map, return from subroutine. A hasn't been modified
    jmp -                       ; if it is not, go to the start of the loop again
+   lda .regular_2_to_3_values, x  ; if we found a translation, load the new value
++  rts


(I did this from memory, so it might not work on the first try, but hopefully it gives you the idea.) This makes the code less maintainable, because now you have to keep track of two completely unrelated lists, but it's just another tradeoff.

Of course, this approach works only if you have fewer than 128 entries to map; any more, and it'll be less memory-efficient than a LUT. If you wanted to go fancier, you could use some kind of tree-based structure, like a trie, but that probably won't do you much good for something as simple as this.


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 28, 2022 12:35 pm 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 690
Location: North Tejas
akohlbecker wrote:
I need to brush up on my data structures... :-) I've been experimenting with a map for the scancode translation and below is what I have. What do you think?


A table containing just the translated codes indexed with the scan code is likely to be smaller than your table of scan codes and translated value pairs. Plus the code to use it is faster and simpler. The pathological case is if the scan codes are scattered within the 256-value space so that the table may be near 256 bytes compared with your size of 2 * number of keys.


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 28, 2022 2:11 pm 
Offline
User avatar

Joined: Sat Jul 24, 2021 1:37 pm
Posts: 282
CountChocula wrote:
akohlbecker wrote:
I need to brush up on my data structures... :-) I've been experimenting with a map for the scancode translation and below is what I have. What do you think?


I think it's fine for such a simple use case. You could save one byte and two cycles by separating out the keys from the values—e.g.:

Code:
.
    ...


(I did this from memory, so it might not work on the first try, but hopefully it gives you the idea.) This makes the code less maintainable, because now you have to keep track of two completely unrelated lists, but it's just another tradeoff.

Of course, this approach works only if you have fewer than 128 entries to map; any more, and it'll be less memory-efficient than a LUT. If you wanted to go fancier, you could use some kind of tree-based structure, like a trie, but that probably won't do you much good for something as simple as this.


Interesting optimization! Thanks

BillG wrote:
akohlbecker wrote:
A table containing just the translated codes indexed with the scan code is likely to be smaller than your table of scan codes and translated value pairs. Plus the code to use it is faster and simpler. The pathological case is if the scan codes are scattered within the 256-value space so that the table may be near 256 bytes compared with your size of 2 * number of keys.


For set 2, unescaped scancodes go from 0x01 to 0x83 and escaped scancodes from 0x10 to 0x7D. Even though the table is scattered, I need to know which ones to translate and which ones to pass through, so I need the full size. Worst case, assuming I'm ok with overflow in case a scancode is bigger than this range, is 258 bytes. The corresponding lists I showed above are 89 bytes total, but the code has to traverse them for every key press.

Now for characters, Set 3 scancodes go to 0x8D. So accounting for shift state you need 284 bytes. Then AltGr and AltGr+Shift is another 284 bytes. Then there are dead keys, for which only a few mappings exist, but the brute force way is four 284 byte tables (in my locale: ~ ^ ¨ `, with and without shift). These are really better with maps because only a few matching keys exist and also you don't need to run the search for each key press, so the time complexity applies to very few cases.

All in all using only tables I can do everything in 1962 bytes. I'm thinking a better solution is to use maps for dead keys, and potentially even for AltGr, as it is a rarer combination. Scancode translation runs every keypress so I'll probably go back to a table, and same thing for shifted and not modified characters.

_________________
BB816 Computer YouTube series


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 28, 2022 2:22 pm 
Offline
User avatar

Joined: Sat Jul 24, 2021 1:37 pm
Posts: 282
Actually, I'm not sure the speed of this matters, I may be trying to optimize away a non-issue :-)

_________________
BB816 Computer YouTube series


Top
 Profile  
Reply with quote  
PostPosted: Wed May 18, 2022 6:39 pm 
Offline
User avatar

Joined: Tue Aug 11, 2020 3:45 am
Posts: 311
Location: A magnetic field
akohlbecker on Mon 25 Apr 2022 wrote:
mapping between CP1252 to the font available on the built-in controller.


I've looked at this problem and my solution is to make the keyboard more like the display and make the display more like the keyboard. When using a legacy keyboard, my preferred solution is to cut off the PS/2 or USB keyboard controller use shift registers. This side-steps many of the recent problems encountered by AndersNielsen with PS/2 clock edges on 6522.

If you don't have the luxury of modifying the keyboard or display, mapping CP1252 to HD44780 is an unenviable task. However, HD44780's Kanji section can be safely ignored because it is mostly useless without the larger sets of Katakana and Hiragana.

I like rudla.kudla's idea of handling two bytes. Indeed, my preferred representation would be synthetic set where one byte is an amalgamation of Roman, Greek and Cyrillic without accents and the other byte has one bit for under-line, one bit for strike-through, one bit for over-bar and five bits for accent. Of most interest to you is accents such as diaeresis and circumflex. This may optionally be combined with one byte for RRGGBBII foreground color and one byte for background color.

Note that the emphasis of my encoding is to populate 2^N representations for display hardware. Many representations mash this together with the conflicting functionality of cursor positioning, inverted video, color attributes, function key codepoints, tab, carriage return and bell. For a bad example, see PETSCII. However, PETSCII mostly works because display hardware has 128 symbols and one bit for inverted video. Given that a PETSCII output stream has one codepoint to set reverse video and one value to clear reverse video, there are 126 remaining codepoints for other functionality.

In the general case, you may have to accept that an input stream and an output stream are not symmetrical. For example, if you have a special symbol, such as carriage return, this will be one less codepoint for display. It may be preferable for output to be "8 bit clean" and have an alternative mechanism for cursor positioning and other attributes. However, this does not resolve the mis-match of character sets. Unfortunately, if the output is HD44780 then many codepoints will map to a dummy representation. This applies to an internal representation of ISO Latin1, CP1252, my 16 bit representation or Unicode. Mis-match can be minimized by using HD44780 or a superset but this may be premature optimization which is unsuitable for video display or UART.

Regarding PS/2 key-code to CP1252 mapping, I am uncertain if the fastest algorithm is premature optimization or not. The very fastest algorithm is an uncompacted table for each type of accent. However, this assumes that you have no other use for ROM. If you only wish to use your system for word processing then a linear search through a compacted table is acceptable because the system has little else to do. Even here, overhead can be minimized by arranging symbols by frequency of use. However, if you want games or multi-media then the least latency is desirable without filling kilobytes of ROM. I considered a Bloom filter. This is a popular choice when the mapping is sparse. It also has the advantage that search time may be constant. However, I don't believe that the mapping is sufficiently sparse for a Bloom filter to be useful. A binary search may be preferable.

_________________
Modules | Processors | Boards | Boxes | Beep, Beep! I'm a sheep!


Top
 Profile  
Reply with quote  
PostPosted: Thu May 19, 2022 7:31 pm 
Offline
User avatar

Joined: Sat Jul 24, 2021 1:37 pm
Posts: 282
Sheep64 wrote:
akohlbecker on Mon 25 Apr 2022 wrote:
mapping between CP1252 to the font available on the built-in controller.


I've looked at this problem and my solution is to make the keyboard more like the display and make the display more like the keyboard. When using a legacy keyboard, my preferred solution is to cut off the PS/2 or USB keyboard controller use shift registers. This side-steps many of the recent problems encountered by AndersNielsen with PS/2 clock edges on 6522.

If you don't have the luxury of modifying the keyboard or display, mapping CP1252 to HD44780 is an unenviable task. However, HD44780's Kanji section can be safely ignored because it is mostly useless without the larger sets of Katakana and Hiragana.

I like rudla.kudla's idea of handling two bytes. Indeed, my preferred representation would be synthetic set where one byte is an amalgamation of Roman, Greek and Cyrillic without accents and the other byte has one bit for under-line, one bit for strike-through, one bit for over-bar and five bits for accent. Of most interest to you is accents such as diaeresis and circumflex. This may optionally be combined with one byte for RRGGBBII foreground color and one byte for background color.

Note that the emphasis of my encoding is to populate 2^N representations for display hardware. Many representations mash this together with the conflicting functionality of cursor positioning, inverted video, color attributes, function key codepoints, tab, carriage return and bell. For a bad example, see PETSCII. However, PETSCII mostly works because display hardware has 128 symbols and one bit for inverted video. Given that a PETSCII output stream has one codepoint to set reverse video and one value to clear reverse video, there are 126 remaining codepoints for other functionality.

In the general case, you may have to accept that an input stream and an output stream are not symmetrical. For example, if you have a special symbol, such as carriage return, this will be one less codepoint for display. It may be preferable for output to be "8 bit clean" and have an alternative mechanism for cursor positioning and other attributes. However, this does not resolve the mis-match of character sets. Unfortunately, if the output is HD44780 then many codepoints will map to a dummy representation. This applies to an internal representation of ISO Latin1, CP1252, my 16 bit representation or Unicode. Mis-match can be minimized by using HD44780 or a superset but this may be premature optimization which is unsuitable for video display or UART.

Regarding PS/2 key-code to CP1252 mapping, I am uncertain if the fastest algorithm is premature optimization or not. The very fastest algorithm is an uncompacted table for each type of accent. However, this assumes that you have no other use for ROM. If you only wish to use your system for word processing then a linear search through a compacted table is acceptable because the system has little else to do. Even here, overhead can be minimized by arranging symbols by frequency of use. However, if you want games or multi-media then the least latency is desirable without filling kilobytes of ROM. I considered a Bloom filter. This is a popular choice when the mapping is sparse. It also has the advantage that search time may be constant. However, I don't believe that the mapping is sufficiently sparse for a Bloom filter to be useful. A binary search may be preferable.


So I'm not using the 6522 for this, but Ben Eater's circuit, which uses 2 chained shift registers to capture whole packets and then send an interrupt based on an RC delay. It works very well! Here it is with a few modifications (adding parity bit check, alignment check, and an Output Enable pin)

Attachment:
keyboard_v2.pdf [76.32 KiB]
Downloaded 31 times


Thanks for the insights on the additional data (setting, colors) needed for display. Not going to put this in the ISR though, as it is application-specific. My idea to handle "commands" like backspace or enter would be to have a scancode handler, earlier in the chain than the character handler, where the app can listen for these non-character scandcodes - or even re-implement the translation state machine if needed. So I would have three available handlers, key pressed, key released and character.

For a better mapping to the LCD I'm considering replacing my character display with a graphics display and use a custom font, which would need 2K of ROM. Also thinking about using the [url=https://en.wikipedia.org/wiki/Windows-1252#MSDOS_extensions_[rare]]MS/DOS extensions[/url] for UI drawing. That is quite the scope creep though :-)

_________________
BB816 Computer YouTube series


Top
 Profile  
Reply with quote  
PostPosted: Fri May 20, 2022 4:23 am 
Offline

Joined: Fri Apr 15, 2022 1:56 pm
Posts: 45
Location: San Antonio, TX, USA
akohlbecker wrote:
So I'm not using the 6522 for this, but Ben Eater's circuit, which uses 2 chained shift registers to capture whole packets and then send an interrupt based on an RC delay. It works very well! Here it is with a few modifications (adding parity bit check, alignment check, and an Output Enable pin)

I've also made a keyboard interface based on Ben Eater's circuit, with the addition of a means to send commands to the keyboard using a pair of 74HC165 parallel load shift registers and a couple of signal diodes for connecting to PS/2 CLK and DATA. These 'send' shift registers share data pins on the VIA with the 'receive' shift registers. I also added a parity check but did that in software.

For me, sending commands to the keyboard was a necessity as the one I have (Periboard-409P) does not initialize to start sending key data until it has received at least one command. However it's also nice to be able to control the indicator lights (e.g. caps lock,) and set the keyboard auto repeat rate.


Top
 Profile  
Reply with quote  
PostPosted: Fri May 20, 2022 9:47 am 
Offline
User avatar

Joined: Sat Jul 24, 2021 1:37 pm
Posts: 282
Could you share your schematic? I've always wanted to include write capability (also purchased the same keyboard that would be more compact that my full-size one, but like you said it doesn't work). Thanks!

_________________
BB816 Computer YouTube series


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: