MicroKey2

Microcontroller board for the 250466+ board to switch functionality through the C64 keyboard.

Introduction

This is an add-on board for the 250466+ board which allows to perform normal and hard resets and to switch between eight Kernals, four SID2 addresses and swap the joystick ports by keyboard presses instead of onboard jumpers. It is the successor of the 1st MicroKey.

While the MicroKey worked as expected, there were some issues with it:

Therefore, the MicroKey2 goes a step further:

Features:

Design

The MicroKey2 board sits on two mainboard headers/sockets which allow it to access all pins from the keyboard connector and the pins to select the Kernal, the SID and the joystick swap.
There are actually two connectors on the 250466+ board, but they are located next to each other, so the MicroKey2 board can access both.
In contrast to the 1st MicroKey, there is a full keyboard connector on the MicroKey2 board. Connecting the Keyboard here allows to MicroKey2 board to connect and disconnect the keyboard from/to the CIA pins.


The Keyboard connector is used to scan the keyboard and the Microcontroller connector is used to switch the Kernals, SID address, joystick ports and perform a (hard) reset.
To perform the keyboard scan, a small microcontroller, namely the LPC824 is used. It's a smallish Arm Cortex-M0+ controller in a TSSOP20 package with 16 general purpose pins.

The RESTORE key is connected to an input pin, EXROM and INTRST are true open drain outputs used to draw the EXROM and RESET lines of the C64 low which is needed for (hard) resets.
The SWITCH output is used to switch the bus switches and the SDA/SCL pins are used to communicate with the I2C port extenders.

To be able to perform a full keyboard scan, the 16bit open drain port expander MCP23018 is used:

The microcontroller communicated with it through I2C and its port A is connected to PB in the C64 while its port B is connected to PA. So A and B are switched and also the pins on port B (PA) are flipped. Both to make routing easier.
To perform a keyscan, port A (PB) is configured to input pullup and port B (PA) to open drain output. By switching all 8 pins of port B (PA) low and scanning port A (PB) for each step, all 64 keys can be detected.

To control the pins for joystick swap, Kernal selection and SID address selection, the 8bit open drain port expander MCP23009 is used:

It is also used to control the bicolor (3 pin) LED but to be able to drive a current through the default LED setup also used in the C64 or Ultimate64, a dual P-FET SI9933 is used.

To be able to connect the keyboard to the CIA, two 8bit FET bus switches SN74CBT3345 are used.

If the "Output Enable" pin OE is high, the keyboard is connected to the CIA, else it's disconnected.

Note that all outputs are implemented as open drain outputs, i.e. they can only pull the signal to ground and need a pullup on the mainboard.
The C64 internal signals like INTRST and EXROM are low active and have pullup resistors. The 250466+ specific signals SID_Ax_SEL, KERNAL A1y, JOYSWAP are designed in the same way: there are pullups on the board and you can either configure these signals by placing jumpers to ground or connect switches to ground. So when using the MicroKey board instead, it also just needs to switch to ground.

Now, the LPC824 has 16 GPIO pins where two of them are dedicated open drain pins which are fully 5V tolerant (i.e. you can apply 5V even if the controller is not supplied). All the other GPIO pins actually have push/pull stages and while they are 5V tolerant as well, this is only true when the controller is supplied. Therefore only the two dedicated open drain pins are used directly to control EXROM and INTRST.

Note that the LEDs actually need a high side output. Therefore the dual channel P-FET Si9933 is used to convert the two open drain LED outputs from the MCP23S18 into highside outputs.
 

A short introduction to the Keyboard Scan on the C64

The C64 doesn't have dedicated hardware to scan the keyboard for key presses. The 64 "normal" keys of its keyboard are arranged in a matrix of 8 rows and 8 columns and these 16 lines are connected to an I/O chip called MOS 6526/8520 which is also known as CIA (Complex Interface Adapter). Note that there are actually 66 keys on the keyboard where the SHIFT LOCK key is just a mechanically locked version of the left SHIFT key and the RESTORE key has its own line independent from the matrix (discussed later).
Anyway, the normal 64 keys are arranged in a matrix like this where the columns are connected to PortA of the CIA and the rows are connected to PortB.

  PB0 PB1 PB2 PB3 PB4 PB5 PB6 PB7
PA0 DEL RETURN Cursor L/R F7 F1 F3 F5 Cursor U/D
PA1 3 W A 4 Z S E SHIFT L
PA2 5 R D 6 C F T X
PA3 7 Y G 8 B H U V
PA4 9 I J 0 M K O N
PA5 + P L - . : @ ,
PA6 £ * ; CLR SHIFT R = Arrow Up /
PA7 1 <-- CTRL 2 SPACE C= Q STOP

Forgive me that PortA pins are rows in this table while the schematic calls them columns (as do I). Just imagine that rows and columns are switched ;)

When using the C64's built in command line interface or BASIC programming language, the keyboard scan is performed by the integrated operating system, also known as Kernal.
Note that while the way that the Kernal performs the Keyboard scan is a typical way, it's not the only way to do it. Actually quite a few games have a very different keyboard scan. Actually a game or demo can decide to not scan the keyboard at all.
I'll not go into much details, but also the joysticks lines are connected to PortA and PortB of the CIA. There is no matrix though, pushing a direction or button just pulls the according line to ground. So scanning the joysticks is generally possible without pulling any CIA ports low. Then again, scanning a paddle or mouse involves switching the POTX/Y multiplexer through PortA6/7. But that's another story.

Anyway, the Kernal keyboard scan is performed in the Kernal's IRQ routine which is triggered with 60Hz frequency also on PAL C64s. It configures the pins of PortA as outputs and the pins of PortB as inputs. The first thing it does is to pull all PA0 pins low to check if at least one key is pressed. If this isn't the case. it stops there. If a key is pressed (in the example F1 which is in row PB4), the routine pulls down each column PAx one after the other. This way, also multiple key pressed can be detected.

It's also worth noting that the Kernal always leaves PA7 switched low. The reasons for this is that the STOP key is in this column. So several routines in the Kernal can check if STOP was pressed outside of the normal keyboard scan.
Now, again, this is a typical way to scan the keyboard, but not the only one. Actually, a game can also decide to configure PortA as input and PortB as output. However, most games at least keep the convention to use PortA as output and PortB as input.
The background for this probably is that, as in its predecessor, the MOS 6522 aka VIA (Versatile Interface Adapter), the two ports A and B are not totally identical. While the CIA datasheet isn't very clear about this, PortA uses open drain outputs with fixed pullups (implemented through N-FETs) while PortB actually uses push-pull stages. But as the CIA is an NMOS device, not a CMOS device, the highside of the push/pull stage is also an N-FET and therefore is pretty high ohmic even when active. Therefore the CIA manual talks of an "active" pullups.
This is why shorting one of the CIA Port pins to ground doesn't do any harm for either, PortA and PortX. As mentioned before, this is actually how joysticks work on a C64. They share lines with keyboard rows/columns and if you press a direction or button, the according line is switched to ground. Which also explains that joysticks interfere with the (Kernal) keyboard scan.
 

The RESTORE key

We already learned that the RESTORE key has a separate line and is not part of the keyboard matrix. Indeed, the RESTORE key on the C64 is not like any other key as its status can't be read. Instead, it's used to create a short low pulse on the NMI (not maskable interrupt) line. This is accomplished by using a Timer IC 556 configured as a one shot multivibrator (yes, that is a thing and no, this is not raunchy). The length of the NMI pulse is determined by R24 and C28 (~29µs).

Also note that the RESTORE line is not directly connected to the trigger input of the 556. The capacitor C29 acts as a high pass filter. I.e. if you press RESTORE, there is a falling edge which leads to a short low pulse on the TRIG input.
I.e. it doesn't matter how long you press down the key, as the only thing that is really used is the falling edge created by pressing it down.
Side note: on older board revisions, the capacitor C29 was pretty small (51pF) which caused the NMI to be only raised if you smashed the RESTORE pretty key hard to create a falling edge with maximum steepness and minimum low level. For my 250466+ board, I suggested to use 2.2nF instead which also triggers the NMI if you press down RESTORE "normally".

Anyway, what is important here is that RESTORE is not only an independent key that is not part of the keyboard scan but also the C64 can only detect that it was (shortly) pressed and can't know if it was released or not. An external circuit reading the state of the RESTORE line directly is not limited like this of course. This makes the RESTORE key the ideal candidate to initiate special functions when pressing it down longer. This is not a new idea of course, so I borrowed it from other Kernal switchers and the like. The idea is that pressing down RESTORE for e.g. 500ms is used as indicator to begin looking for special key presses to switch functions. Also pressing it down a few seconds can be used to initiate a reset or a hard reset.

Keyboard Scan done by MicroKey2

Since the MicroKey2 can completely disconnect the keyboard from the C64, it can perform a full keyscan on its own without time restriction or the need to avoid interference with the Kernal's scan.
Each time the Restore key is pressed, the following happens;

The actual keyscan is similar to the scan performed by the Kernal:

When Restore is released again

Hard Reset

The MicroKey2 can perform normal internal resets for two reasons: firstly, switching the Kernal without performing a reset could lead to an undefined CPU state (i.e. one opcode ist fetched from the old Kernal and the next one for the new Kernal etc.). This is also important for configuring the last selected Kernal after a cold start. Secondly, MicroKey2 is about avoiding case modifications etc., so being able to perform a reset without a dedicated switch is just very handy.
Now, performing an internal reset is very easy as it just involves pulling down the INTRST line low for 200ms. So, what is a hard reset and why would anybody want one? To answer this, first let's have a look at the (Kernal's) reset procedure.

After a reset, the Kernal checks if it can detect the string "CBM80" at the address 0x8004. If the string is detected, the two bytes at 0x8000 are interpreted as reset vector and jumped to. The idea is that the string "CBM80" indicates the presence of a ROM module attached to the expansion port which is mapped into an 8kB RAM page at 0x8000 by pulling EXROM low on the expansion port. Now, back in the 80s this mechanism was misused by to implement a reset protection. The according programs just put the string "CBM80" in the RAM at 0x8004 and put the address of some internal function (typically program restart) at 0x8000. So when causing a reset by pulling INTRST low, the program would shortly lose control but the Kernal would call the program's reset handler instead of performing its own.

Now, the idea of a "hard" reset is to pull EXROM low a bit longer than INTRST. This temporarily maps a non-existing ROM module into the 8kB page at 0x8000. So the Kernal can't see the CBM80 string in the RAM at 0x8004 because it just reads nonsense from the expansion port. Therefore it continues with its own reset routine and the reset protection is circumvented.

The typical time for the EXROM delay is 200ms which works fine for the normal Kernal. Alternative Kernals like Speed DOS have a slightly different timing, so they may have already started the RAM check while EXROM is still low. This leads to an 8kB page missing and so instead of "38911 BASIC BYTES FREE", only 30719 bytes are reported. While I'm not sure that there is an EXROM delay that works for every possible Kernal, at least the software controlled EXROM line allows exact control over the timing. Note that there actually is a hard reset circuit on the 250466+ board, but it's implemented as HW delay which is neither very exact nor can it be changed easily.

Usage

If RESTORE is pressed down longer than 500ms, the currently active LEDs is switched off and there is a keyboard scan performed. Releasing RESTORE ends the keyboard scan and switches on the currently active LED.
If one of the supported keys (the 8 numerical keys 1..8, the four functions keys and J) is detected during the keyboard scan, the according function is selected and confirmed by a blink code on the bicolor LED.

Joystick Port Swap

If J is pressed during the keyboard scan (initiated by pressing RESTORE for > 500ms), the joystick ports are swapped. The swapping is confirmed by a single blinking of the originally active LED followed by a single blinking of the other LED. After releasing RESTORE, the permanently active LED is switched to indicate the port switch. I.e. if LED1 of the bicolor LED is red and LED2 of the bicolor LED is blue, red/LED1 means that the joystick ports are not swapped while blue/LED2 means that the joystick ports are swapped. Note that there is no reset.

SID2 address selection:

Pressing the four function keyse during the keyboard scan switch the address selection lines A5_SEL and A8_SEL for SID2. This allows to select the four addresses 0xD400 (dual mono), 0xD420, 0xD500 and 0xD520. F1 selects 0xD400 (dual mono), F3 selects 0xD420, F5 select 0xD500 and F7 selects 0xD520. A change of the SID2 address will be confirmed by a blink code on the 2nd LED. Blinking once means 0xD400, twice means 0xD420, three times means 0xD500 and four times means 0x5D20. No reset is performed.

Kernal switching

The three address lines A13/14/15 on the 64kB Kernal (E)EPROM are used to select between 8 Kernals. The numerical keys 1..8 are used to select eight different Kernals. After each detected change, the current selection (1..8) is used to blink the first LED. E.g, if you selected Kernal 7, the LED will blink 7 times. As soon as RESTORE is released, a reset is performed and the Kernal is switched.

Note that to allow multiple selections and reduce NVRAM write cycles, the new configuration (for all the functions) is only actually used (and stored) after releasing RESTORE. Therefore, the reset after a Kernal switch is also only performed after releasing RESTORE. Besides, releasing RESTORE also switches on the currently active LED again, so a delayed releasing of RESTORE is helpful to read the blink codes.

Reset

If RESTORE is pressed for 3s without any other activity (key presses or blink codes) and then released, a normal RESET is performed. It also possible to press R during the keyscan instead.
If RESTORE is pressed for 5s without any other activity (key presses or blink codes), a hard reset is performed without releasing RESTORE. It also possible to press H during the keyscan instead.

Status Display

As mentioned before, only the joystick port swap is permanently displayed by the color of the bicolor status LED. To display the currently selected Kernal and SID address, the keys K (Kernal) and S (SID) can be pressed during the keyboard scan. This will result in blinking the first LED for 1..8 times to indicate the selected Kernal or blinking the 2nd LED for 1..4 times to display the selected SID address.

Hints and Issues

Repository

All files related to this project can be found in the BitBucket repository
https://bitbucket.org/fade0ff/microkey2

License Information

This is a spare time project I did without any commercial interest.
Everything is released under the Creative Commons CC-BY license.

Creative Commons License

In a nutshell this means that you can do share, modify and use everything released under this license even for commercial projects.
You just need to give me appropriate credit, indicate what changes you made and agree not to try to force a more restrictive license on my work.
See the CC BY license for details.


Home