Auto binds to pairing device
gampad activity shows here
and see serial monitoring
for more devices & infoTechnical projects created by Dave Van Wagner
Auto binds to pairing device
gampad activity shows here
and see serial monitoring
for more devices & infoUp, Left, Right, Down, Fire (M5) and second button (click joystick). Not just a d-pad. Is also mirrored as xpot, ypot, so can do paddle games, or analog joystick games.
Go play some games!
Link: github.com/davervw/m5_minijoystickc_gamepad
Also posted on M5Stack M5Burner
It's finally here! After years of anticipation and a weird workaround, the ideal solution is implemented and working. I can finally use my Palm Portable Keyboard (PPK) with my custom emulators for Commodore 8-bits. Github: ppk_bluetooth_for_cbm
![]() |
| PPK BLE adapter for wearable/portable emulators of 8-bit CBM systems |
The Palm Portable Keyboard was released for the Palm Pilot PDA lineup in late 1999. I remember getting mine right away for US$99 from Circuit City. The Palm IIIc came out in early 2000 and got one of those too. To date my longest use was to write up notes on a plane ride. (Let's just not think too much about me leaving/losing the keyboard in the seatback pocket. Ouch! Rushed out and got a replacement at $99.) Back in the day, it was mostly for the cool factor. I loved gadgets, and between the Palm III color and the keyboard, and the Kodak color camera, I had some pretty cool gadgets. I knew I was cool, even if no one else believed.
Fast forward to 2020 when I'm enjoying building emulators for Commodore 64, 128, and Vic-20 on wearable and portable platforms based on ESP32 and similar. One real need was to input into these devices. With the STM32F4 I had gotten USB-OTG to work, and mapped a standard USB keyboard to trick the Commodore into thinking a normal matrix keyboard was present. The Teensy 4.1 target was a direct port of that keyboard code as it had an optional external USB host port. Also worth a mention of leveraging the same algorithms and data to support keyboard mapping with my Typescript port.
![]() |
| STM32F4 with USG OTG |
Once I had a wearable emulator on my wrist, I also looked for keyboard solutions. On one July 4th holiday vacation I was able to research and implement a web page helper that translated keystrokes in the keyboard and sent them as Commodore scan codes over USB serial to the emulated system. And I also found a way to leverage this on my Android phone so it would host the web page and transmit over its USB serial (OTG) to the emulated target host.
![]() |
| Wires and phone required in 2023 |
More development later, and I had an adapter for a real Commodore keyboard to serial TX line (plus 5V/GND input), that could plug into the Grove connector of the M5Stack targets. Sure it was fun to plug in a real C128D keyboard into a system with a 2" LCD; quite the show off I am, looking for a good laugh. But it also worked!
More work (and purchases) later and I had M5Stack's CardKB keyboard, and wrote drivers for that to convert to scan code presses and releases for Commodore.
And then I created a Bluetooth (BLE) adapter to convert serial/I2C connections to a wireless connection. The keyboard and the wrist emulator could be used with direct wiring. Much to the delight of portability and more showing off.
Out of necessity, a standardized protocol was born by accident. Standardized to my implementations only so far, but I was for sure the beneficiary of such technological advance.
The protocol depends on two features:
1. Serial transmission of changes
2. List of Commodore 64/128 scan code values, comma separated, newline (serial only) terminated
Example:
15,7
which means left shift and up/dn key (= cursor up)
The emulators targets (see Unified branch of github.com/davervw/c-simple-emu6502-cbm) support this protocol over USB serial, Grove port (UART RX), and custom BLE service depending on hardware capabilities. Internally the list of scan codes is used to provide feedback from I/O read/write processing from the 6502 checking to see what lines are connected. So while I could have implemented a more binary or compact format of transmitting this data, I enjoy the diagnostic capability of seeing the values clearly when necessary.
The keyboard hosts utilizing this protocol include the key scan codes helper for the web (USB serial out), two instantiations of 25-pin matrix to UART TX supporting real Commodore keyboards, and a BLE bridge firmware with a Grove connector (e.g. on M5Stick-C).
The scenarios that work include
1. Type from my Windows keyboard, adapted to scan codes sent over USB serial (any of the targets). Keyboard and target are both wired via USB. A web page is making the translation and bridging between the keyboard and USB serial.
2. Connect Commodore keyboard adapter directly to Grove port on one of the various M5Stack devices: M5Core, M5Core2, M5CoreS3, Tab5. This looks like a Commodore keyboard wired through a mess so tied to the target.
3. BLE adapter takes scenario #2 and cuts the wire between the adapter mess and the target. The target appears free and clear of any wires (if self powered). The adapter can be tucked into/under/next to the keyboard. While this acts as a Commodore BLE keyboard, the keyboard itself has a mess of wires, power supply, and adapters.
The BLE adapter actually accepts THREE different inputs. USB serial (from PC host), Grove UART RX, and Grove I2C for CardKB). Note that CardKB isn't using my keyboard standard as an input because it has its own protocol, and I didn't feel like reprogramming it. CardKB drivers are in both the BLE adapter, and in the emulator itself for supporting direct connection. Both CardKB uses are converting and outputting the Commodore scan codes internally.
4. USB host adapter takes a standard USB keyboard and converts to UART TX. This can be cross wired from Grove port to Grove port, or be used in conjunction with #3 (great!! more adapters to the mess) to appear as a USB to BLE wireless adapter.
So I've created an ecosystem of keyboard compatibility built on a custom "standard" so parts can work together and interchange/swap. It allows for flexibility, and interconnections. And when a new keyboard input source comes along (like PPK), implementing the current standard brings extra value to the table.
The pre-existing firmware for the Bluetooth adapter as developed by pymo took the obvious choice - present as a standard Bluetooth HID keyboard. That solution works for Windows, Mac, Android, iPhone, and others. It's a great standard. But it doesn't directly translate to a great Commodore keyboard experience. And I don't have a general BLE HID solution integrated into my emulators yet either.
With the way things are with my custom Commodore scan code protocol, it made sense to revise the PPK BLE adapter directly, take advantage of all keys present including Fn and special purpose keys, and map them to what makes sense for Commodore. And add extra value too!
Let's compare to the layout of my favorite Commodore system, which is a superset of C64
These are the symbolic key mappings I came up with
The end result is a full-featured Commodore 128 keyboard for use with Commodore 128 emulation. (And some standard ASCII keys [{}~`|] not present with Commodore, so more useful in non-Commodore emulation). And by the way, the Commodore 64 and Vic-20 emulations map the extra C128 keys to its own matrices automatically -- unlike a real Commodore 128, etc. For instance, the four arrow keys and numeric keypad don't work in C64 mode on a real C128, but they do in the emulated system -- appearing as if the original 64 key matrix keys were pressed instead. This is implemented in the emulator itself. The keyboard is optimistic and reports everything expecting a C128 on the other end. The emulator maps extra keys down to C64 keys, and if in Vic-20 mode, unscrambles into the Vic-20 scan codes too.
(C64 and Vic-20 share the same exact keyboard, but the lines were reordered when connected to the I/O chips. The same keyboard would work in the C128 except for the missing keys - the C128 is a superset. How do I know this? I use both a real Vic-20 and real C128D keyboard interchangeably connected to my self designed Commodore to UART Tx [Grove] adapter, and they work interchangeably in my emulator. How cool would it be to build a C128D replacement keyboard from a Vic-20 keyboard in case? That would blow some minds.)
1. Plug in the 3D printed Bluetooth adapter and circuit into the PPK to automatically turn it on and start transmitting that it is in pairing mode (rapid Green flashes).
2. Turn on or reset the Commodore Emulated system (e.g. M5CoreS3 or Sunton)
3. Wait for Commodore to boot
4. Should be paired (occasional Green flashes)
5. Type away!
![]() |
| Sunton 7" tablet (Perler bead frame) and PPK BLE |
![]() |
| Keymap Editor for C64 |
The original intent is to put punctuation keys where they are supposed to go compared to modern keyboard layouts. If one is using an authentic keyboard or equivalent then you may reach for the wrong key if trying to type an @, quotes, or colon.
![]() |
| Control keys revised with numeric keypad |
One scenario you may not have thought of is to create a temporary palette of Commodore graphics characters in a custom configuration for programming a game or screen, where the graphics characters are not presently mapped to the keyboard, or not in intuitive locations. You can always restore to the default keyboard mapping, or load another preferred one.
The popular scenario for non-US users is for international keyboard layouts: to match regional differences in layouts, and to support characters not present in the US character map, including corresponding character font image differences. There are existing ROMs that include modified layouts and character images and are available at zimmers.net. Some have slight differences, and some have great differences; changes may also be focused on the upper/lowercase character set in comparison with the upper/graphics set.
![]() |
| Danish ROMs example |
Alternatively to having different ROMs, one could modify in RAM including the character set with a font editor, and manually revise the keyboard layouts with the utility I am describing here.
![]() |
| Font editor is also available |
Here are some step by step instructions for remapping the punctuation
1. Start your Commodore 64
2. Insert c-keymaps disk (or 64keymaps.prg)
3. LOAD "64KEYMAPS 49152",8,1
4. SYS 49152
5. Navigate to full colon key with cursor keys
6. Type new key to use: single quote ', and it will appear in its place
7. Press cursor left to return to that key
8. Press home to switch to mapping when shift key is pressed
9. Type new key to use: double quote ", and it will appear in its place
10. Navigate to the punctuation in the numeric key row (still in shift key map), and update them according to your preferences, e.g. !"#$%^&*()
11. When changes are to your liking, press F7 to Save
12. The program exits to BASIC showing you a driver program entry and has typed SAVE" for you. Complete the filename, end the quotes (probably using the original keystroke if not active yet) and add comma 8 for the disk drive, and press RETURN.
13. Once saved, you are welcome to VERIFY as well, and/or make sure your drive is not blinking red
14. To activate the driver, one need only RUN this program in memory. Validate the keys are mapped as expected.
15. When your computer is restarted, you need only load and run the driver you saved to apply the key mapping. The editor is the 49152 program.
16. Whenever the editor is in upper memory, you can return to it with SYS 49152. Whatever active keyboard mapping is in effect will be displayed for editing, whether that be from ROM or RAM.
17. To return to the ROM keyboard mapping, press STOP+RESTORE and verify normal operation (but if the keyboard is not working, or system is locked up, you may have to reset the C64 computer).
18. Whenever the RAM driver is running, editing will use the new mapping, but changes are not applied until you press F7 for Save. When the program for the driver is displayed be very careful not to LOAD over it (like a directory), or your system will lock up because the system was relying on the keyboard driver between addresses $0800 and $09FF. To avoid this, press RUN or STOP+RESTORE first before loading a new program. RUN will move the start of BASIC programs up to $0A00.
| Character selection "picker" |
How do you write a snake game? Very carefully. ;-)
Open source at GitHub
For me, it started as an idea to make a featured game for my handheld Commodore emulator platform, most importantly the ones I can easily transport on my person. I've written a simple but not complete Commodore emulator that behaves well on ESP32 and other available microcontrollers. One example hardware platform is the M5Stack Fire that I can stick in my pocket and has physical buttons for inputs.
I was away on vacation with the family when I came up with the idea of replicating the snake game. I was yearning for a good coding puzzle, and this was it!
So I started brainstorming on how one would accomplish it.
First, I decided to just use BASIC. It comes with the Commodores, and is relatively easy to work with. I only have 40+ years experience so I should be able to get by. So no machine language this time.
What system should I develop for? Vic-20 is good for games as the text screen is small (22x23) and the characters are big, easy to see.
The key to snake is that each time it eats food it gets longer, so the snake is bigger and bigger as the game progresses, becoming an obstacle to your own movements. As the snake moves, it inches along, the head advancing in one direction, and the tail catching up to where its next to last segment was. So constantly the head and tail are both moving, and we have to keep track of where they are. The answer is a circular buffer! Segment locations will be stored in a circular buffer.
What data structures do we have with BASIC? Just arrays. Can we implement a circular buffer with an array? Certainly, the array must be pre-allocated (DIM) with a fixed size, and keep track of the head and tail of the snake for the extents of the segments. This is how you would do it in Pascal or C with an array, so BASIC is not much different.
How big should the array be? Let's go big and say the snake can be as large as the screen. But we want a title and score on the screen too, so let's say the bottom line is reserved for those. That leaves 22x22 locations for the segments = 484. Let's plan on keeping an offset into screen memory in each array element.
How should game inputs work? Since my target platform has three buttons pre-configured for Up/Enter/Down, I want to use those, specifically Up/Down since I don't have access to four direction buttons. I intend to use those buttons to rotate the direction left or right relative to the snake's current direction.
How shall we display the snake? I bring up a PETSCII chart and look at the graphics characters. I pick a circle outline as the head, a solid circle as body segments, and a graphic X as the death character. Later in the process I chose the diamond as the food character.
So first step I implement the screen layout, place a single segment in the center of the screen, and implement moving around every second. With some fine-tuning I get it working well, including implementing dying if hit the edge of the screen. I am tracking horizontal position in X (0 to 21) and vertical position in Y (0 to 21) with DX and DY being -1, 0, or 1 each. The D stands for difference or direction. Moving is as easy as adding DX to X, and DY to Y, then making sure it is still within bounds, otherwise dead.
Quickly I found that allocating floating point values for the snake segments caused the original Vic-20 to run out of memory, so the array was changed to DIM S%(SZ) to use 2-byte integers instead of 5-byte floats.
The basics are in place, but the snake is not growing yet. Next the circular buffer is implemented, and I implement computing the length of the snake for the score. It took a few tries to get right, and once finished, I realized I could just track the score/length separately without all that fancy work, but it's done, so I let it be.
If the next position is another character or out of bounds, the snake dies. If the next position is not food, the snake's tail is shortened so the snake appears to move. But if the next position is food, then the snake appears to grow because the tail remains in place. The head is drawn at the next position.
Between moves there is a delay of a fraction of seconds. And as the snake gets longer, the delay gets smaller with a minimum delay to speed up the game, but keep it reasonable.
It took many incremental tries to get everything just right, took the opportunity of beta testing and feedback from my son, and addressed stuff including an opening information screen, the title/copyright/score bar, timing, and fixing bugs. The bugs included running into your own tail that was about to move -- that is allowed, testable by spinning in a tight loop when 4 segments long.
For inputs on a standard Vic-20, I remembered that cursor keys use shift to go the opposite direction. That would be unnatural to require on a real Commodore, so I added Z and / (slash) as alternate inputs for rotating the head. These keys are at opposite sides of the keyboard, so easy to indicate turn left or turn right. (If someone doesn't like the key layout, it's in BASIC, easy enough to change.)
During the process of refinement I added random colors to the food, adjusted the randomizer to pick different colors than the head/tail of the snake so it appears to change, and the snake picks up the color of the food for an interesting effect. Also the background color is avoided so the snake doesn't become invisible.
Next added Vic-20 joystick support. Joystick support is implemented to direct the snake the literal direction of the joystick instead of rotating. And avoids the direction opposite that the snake is already going so it impossible to reverse onto itself (otherwise the snake would die more often). Also because of how the joystick and the keyboard share some input lines, it is necessary to disable some keyboard lines to read the joystick successfully. Pressing STOP will break the program, but not reset the keyboard. This renders some keys unusable until STOP+RESTORE resets the keyboard.
Once the Vic-20 version was stable, source was pushed to GitHub.
Then the game was ported to Commodore 64 with 40 column screen.
And after that, Commodore Plus/4 and 16 port. initially with a machine language routine for reading the joystick until realizing that support is already in BASIC.
Next came a Commodore PET port designed to support most if not various models all running Commodore BASIC, including 40/80 column models, and actively switching to the uppercase/graphics character set in case a model defaults to lowercase. Since the PET doesn't normally have a visible border, inverse spaces are placed around the edges to contain the snake a bit more. Some screen real estate is lost but playability is gained.
I fell in love with the green on black look of the PET game, mostly for nostalgia, but to me it just looks perfect! And that's when I adjusted the PET version to also run successfully on Vic-20 and C64. It happens to work on C128 and Plus/4, 16 as well. The PET version doesn't support joystick though, is all keyboard. Detection of C64 turns colors to green on black, and Vic-20 does white on black.
Then I created Snake targeting my cbmish-script for the web...
And I'm still playing this game often daily...
Here is a single player version of the classic snake game that I have recreated in BASIC for the Commodore Vic-20. Some may remember snake from the Nokia classic phones (1998) or from Atari VCS 2600: Surround (1977).
I have a Commodore watch platform (M5Stack with watch wristband running my Commodore emulators) which I can run some simple programs. The purpose of developing this game was to create something I could play on my watch with just two buttons.
Apparently my high score is 73. Try to beat that!
Open source (MIT License) and binaries including disk image are available at github.
Update 2025-04-16: Also ported to C64 for those so inclined.
Update 2025-04-19: Ported to Commodore PET and TED systems: Plus/4, 16, etc.