Tuesday, August 15, 2017

On-screen keyboard display

Today I more or less got the on-screen keyboard working for the MEGA65:



Not only does it appear as a nice alpha-blended composited display, it also shows which keys are being pressed, even if not being pressed on the on-screen keyboard (more on input modes in a moment):


Some might be asking why a computer with a keyboard needs to have an on-screen display in the first place.  This is indeed a very good question, with several possible (if not plausible) answers:

1. If you were using a C64 keyboard instead of a C65 keyboard, you might like to press one of the additional keys.

2. If you are using a Nexys4 board, and can't remember the key mapping, you can now mash keys until you see the one you want toggle.

3. You might have a broken key, and need an emergency rescue.

4. You might be playing a game, and find it is just too inconvenient to lean forward to press a key on the keyboard, when you could instead do it from your joystick.

5. You might be using a model of the MEGA65 that completely lacks a keyboard by default, such as a tablet or phone form factor.

6. You might want to just show off when people complain that 8 bit computers can't do advanced things like composited video.

As I say, while some of those might be possible, they might not be plausible.

Now, for how this works behind the scenes.

First, this is the fourth layer of compositing that the MEGA65 supports. The other layers are:

1. Bitplaned sprite mode, which allows modifying colours of underlying objects.

2. Alpha-blended text mode, that allows pixels to shade between the foreground and background colours.

3. Matrix Mode, that displays the serial console as a composited overlay, not dissimilar to what the on-screen keyboard does.

Like Matrix Mode, the On Screen Keyboard (OSK for short) is a post-process composited display.  It takes the output of the VIC-IV and Matrix Mode, and overlays the keyboard display on top.  It goes on top of Matrix Mode, because you might need to drive Matrix Mode via the OSK if you don't have a real keyboard available for some reason.

Also like Matrix Mode it has its own character set ROM (currently the ASCII 8x8 font I created for the MEGA65 text editor).  It also has to draw the complete keyboard layout, which is procedurally generated.

I could have done it using a bitmap instead, which would have been more flexible for the base layer, but would have also required 640x145 = ~11.3kB, unless I compressed it. More the point, it would not be possible to decide to show individual keys highlighted to show when they are being pressed.

I started out by thinking about how I could generate it procedurally.  I began with the ASCII-art keyboard layout from the C65 specifications guide, and
modified it slightly to better suite my tastes:

----+    +----+----+----+----+    +----+----+----+----+    +----+----+----+----
RUN |    |ESC |ALT |ASC | NO |    | F1 | F3 | F5 | F7 |    | F9 | F11| F13|HELP
STOP|    |    |    |DIN |SCRL|    | F2 | F4 | F6 | F8 |    | F10| F12| F14|   
----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----
 ~  | !  | "  | #  | $  | %  | &  | '  | (  | )  | {  |    |    |    |CLR |INST
 _  | 1  | 2  | 3  | 4  | 5  | 6  | 7  | 8  | 9  | 0  | +  | -  |GBP |HOME|DEL
----+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+----
TAB    |    |    |    |    |    |    |    |    |    |    |    |    | PI |
       | Q  | W  | E  | R  | T  | Y  | U  | I  | O  | P  | @  | *  | ^  |RESTOR
----+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+------
CTRL|SHFT|    |    |    |    |    |    |    |    |    | [  | ]  | }  |
    |LOCK| A  | S  | D  | F  | G  | H  | J  | K  | L  | :  | ;  | =  |  RETURN
----+----+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+----+----+----
    |       |    |    |    |    |    |    |    | <  | >  | ?  |      |    |
 M= | SHIFT | Z  | X  | C  | V  | B  | N  | M  | ,  | .  | /  | SHIFT|  UP|
----+-------+-+--+----+----+----+----+----+----+----+----+-+--+-+----+----+----
              |                   SPACE                    |    |    |    |
              |                                            |    |LEFT|DOWN|RGHT
              +--------------------------------------------+    +----+----+----

Note that I have also trimmed the left edge and right edge from the key art, and replaced some of the more exotic characters to descriptions of them.  This is to keep the result to 80 columns wide (as the compositor expects 80 columns), and to fit the character set I already had (since we are still not totally sure that it is safe to use the C64 character set without a license.)

I then wrote a C programme that directly reads this ASCII form of the keyboard and creates a packed version of the key labels, ignoring all the +, - and | characters that draw the outlines of the keys, and produces a simple packed format, where multiple spaces are run-length encoded.  The whole thing packs to about 400 bytes.

Then I hand-wrote the key mapping for each discrete key on each row using the keyboard matrix position numbers that the MEGA65 internally uses, numbered from $00 - $47.  If a key was wide, it had $80 added to it.  I probably could have figured out a way to automate the table generation, but for the small size, I figured it wasn't worth it.  Also, even though I made a few mistakes initially, they were easy to find and fix by pressing keys and seeing if the correct one lit up on the screen.

This gave the following table of 7 x 16 entries:

3F,7F,47,42,50,40,7F,04,05,06,03,7F,44,45,46,43
39,38,3B,08,0B,10,13,18,1B,20,23,28,2B,30,33,00
C1,3E,09,0E,11,16,19,1E,21,26,29,2E,31,36,D1,7F
3A,0F,0A,0D,12,15,1A,1D,22,25,2A,2D,32,35,01,01
3D,8F,0C,17,14,1F,1C,27,24,2F,2C,37,B4,52,7E,7E
7E,7E,7E,3C,3C,3C,3C,3C,3C,3C,3C,3C,7E,53,07,02
7F,7F,7F,7E,7E,7E,7E,7E,7E,7E,7E,7E,7F,7E,7E,7E

The astute will note that there are only 6 rows of keys, and that the bottom row looks rather repetitive.  This is because $7E is a special value that means "a non-existent key, but with a horizontal line drawn on the top edge" and $7F means "a non-existent key, with no horizontal line drawn on the top edge".  This sequence draws the bottom edge on the space bar and the cursor keys in the last real row.  This is also the reason for the $7F's on the first row, to leave the tops uncapped on the non-keys between the groups of keys there. If we used $7E, they would have been closed at the top.

The $C1 at the start off the third row is the TAB key, which is wide, so is really key $41 + $80 = $C1.  The wide flag causes the key to be drawn 150% wide, and for the text labels to be offset by half a character, so that they don't get drawn over the vertical lines between keys.

For wider keys, like RETURN or SPACE, the same key value is simply repeated enough times to get the required width, and the logic that draws the boxes around the keys knows that this means a single longer key.

It was then a bit of fiddling to get the VHDL code working correctly to draw all this, although the end result ended up being only 400 lines.  To speed up development, I wrote another little program that could be fed the output of the GHDL simulation of the OSK, and produce a PNG image of the OSK.  This allowed me to find and fix bugs in a seconds instead of the almost hour it takes to synthesise the design to FPGA. Here is an example of the output of the simulation:


Here you can also see several keys being held down with the respective keys being shown in reverse video with a thin border.  This was one off my goals to provide nice visual feedback of keyboard activity, and that the procedural rendering allowed without great trouble.

All in all, I am pretty happy with the result.  The keyboard is clear and accurately laid out, nicely composited over the display, and yet still has a distinctively 8-bit feel to it -- and if we want to change the layout a bit, it is as easy as changing the text file that is pre-processed to produce the packed representation.

Next step will probably be to add a mechanism to have a "cursor" in the form of a highlight key that is used to navigate around the OSK, so that keys can be pressed using a joystick or other input device.  This will be in addition to touch-screen support, that I hope to add in due course.

No comments:

Post a Comment