PS/2 to C64 Mouse Adapter
A pixel artist friend of mine wanted a mouse to try his skills on a real C64. I thought I could help him by making an adapter that would allow a regular PS/2 mouse to be used with a Commodore 64. The most popular and supported C64 mouse is 1351 mouse which uses analog capabilities of joystick control ports to send movement data. Same mouse also has a joystick compatibility mode that emulates joystick tilt in response to mouse movements. A PS/2 mouse uses a serial protocol to send digital packets that contain information about current counter values and status of the buttons. A 1351 mouse sends information in the form that C64 analog inputs can read. [M]ouse, the coupling device, communicates with a PS/2 mouse on one side and presents itself as a 1351 mouse to the other side.
This work is based on the pioneering design by Levente Hársfalvi, Serial mouse interface for Commodore. Although the element base and interfaces supported in his project are different, his brilliant work served as inspiration and basis for the development of [M]ouse.
HardwareHardware consists of ATmega8 microcontroller, filtering caps, several resistors and connectors. The schematic is basically an adaptation of Levente's design to a more modern microcontroller and PS/2 interface. Everything is assembled on a small homemade PCB. Programming pins were only used for fuse bits setting and flashing the bootloader. All firmware programming was done via the serial interface through two temporarily tapped wires.
The circuit contains an additional 5V regulator because I was worried about implied 50mA power draw limitation on the C64 control port. In reality, this limitation is purely hypothetical and the mice that I tried never sucked too much power anyway so this regulator is not needed.
ATmega8 is clocked by its internal 8MHz RC oscillator. It is expected that the device will be used in room conditions with more or less stable temperature and frequency stability is not going to be an issue. To minimize error rate with a 8MHz oscillator, 19200bps serial speed is chosen for bootloading and debugging.
PS/2 Protocol and Mouse InterfacePS/2 protocol and common mouse negotiation procedures are best described by Adam Chapweske in The PS/2 Mouse/Keyboard Protocol paper. Electrically PS/2 interface needs open-drain lines with pull-ups. ATmega8 doesn't have open drain outputs (except for TWL pins which can't be used for PS/2 interfacing). In this project open-drain pins are simulated by dynamically switching between active low and hi-impedance states. The implementation supports two-way communication with a PS/2 device and is interrupt-driven by PS/2 clock on INT0 and by Timer0. Read operations are nonblocking; write operations await completion. The PS/2 software module can be used to interface to any PS/2 device be it a mouse or a keyboard.
Higher-level mouse functions are supported only as much as required to properly boot and configure a regular 3-button mouse.
1351 Mouse1351 Mouse is said to have analog interface, which is however not entirely true. It's based on a premise that C64 analog-to-digital converter in fact measures the time it takes to charge a capacitor (supposedly, through a potentiometer in an analog paddle). By detecting the moment of the beginning of a measure cycle, it is possible to quickly charge the capacitor to its maximum value at any given time. How to interpret the measured time is entirely up to C64 software. 1351 mousing software interprets such changes in time as mouse movement.
Here's an illustration from the original U.S. patent 4,886,941 best describing the workings of the 1351:
A measurement cycle takes not less than 512 microseconds. First 256 microseconds are spent by SID making sure that the capacitor is discharged. The following 256 microseconds are spent waiting until the capacitor charges to the threshold value Vth. The time when Vth is reached is marked and is used by C64 software as analog input data. Instead of using a large ohmic potentiometer, a 1351 mouse rapidly charges the capacitor at intervals between 320 to 448 microseconds from the beginning of measurement cycle. If this is a POTX line and a mouse is being moved right, the interval from 0μs to the moment of 0-to-1 transition will keep increasing until it reaches 448μs, then it will jump back to 320μs and so on. If a mouse is being moved left, the interval will decrease until it hits 320μs and then it will jump to 448μs. The faster the movement, the faster the change. Similar readings could be obtained from a potentiometer without angular limits: the resistance would go up and then hop back to minimum after 360 degrees. In pure terms it can be said that transition delay is zero_point plus (axis_counter_value modulo 128) microseconds. That's all there is to the magic 1351 mouse.
Measuring cycle is sensed by INT1 line, the interrupt is set to trigger on negative edge of the SENSE/POTX line: this happens when SID initiates a new cycle and shorts its measurement capacitor to ground. In order for INT1 to trigger, POTX line must be pulled up and this is done by initial setting of POTX/POTY output lines to 1 (see schematic, code).
INT1 handler loads OCR1A/OCR1B counters with the values that were precalculated based on PS/2 movement counter values. It also makes sure that OC1A/B (POTY/X) lines are outputs and cleared to "0", starts the timer and returns. This is the only time-critical part of the entire project. POTX/Y lines are then asserted high by output compare unit hardware strictly at required times without any software interaction. Since the timer period is 128 times longer than measurement cycle it's safe to just leave it running until the next measurement takes place. A new INT1 will occur and timer will be reset and restared. High level on POTX line also serves as a pullup which is necessary to detect a negative transition on the INT1 line.
PS/2 counter data is reported asynchronously, from the main program loop. Internal counter values are updated accordingly and OC1A/B load values are precalculated to be ready at the next INT1 cycle.
The button signals are "wired" to FIRE/UP/DOWN joystick lines.
Joystick mode (1350 mouse mode)This is the simplier mode. Every time a mouse movement is reported, a joystick line responsible for that direction is closed (switched from hi-Z state to output low) for a brief period of time. Buttons are wired to FIRE and POTX lines. The state is reset back to high-Z by OCR1A interrupt handler which is disabled in other modes.
ConfigurationThe adapter has minimalistic interface which consists of only one button which isn't supposed to be pressed. Normally it's used to reset the device and bootload the flash, but it also can be useful for reconfiguring the mouse on the fly. Configuration can be changed by holding one or several mouse buttons for a few seconds after turning the C64 on, or after pressing the reset button:
- No buttons — boot normally, 1351 mode, normal speed
- Right Button — boot in Joystick mode
- Left Button — boot in Fast mode
- Middle Button — boot in sloow mode
Chord — doodle
VT220 Doodling AppWhile debugging I wrote a little toy for mousing directly in a VT220-compatible terminal window. The code is left in and can be activated by chording all 3 mouse buttons together while booting. Since serial communications are relatively slow they get in a way of normal operation, so this mode is only made optional.
- Schematic and PCB drawing in Eagle CAD format:
- Schematic only in PDF format [pdf]
- Source code: Google Code project mouse1351 [Updates]
- Source documentation [doc]