Tiny64

Some kids grew up with the Macintosh, some with the Apple II. I grew up with the Commodore 64. Yntil I upgraded to a hand-me-down 386SX, this was my home computer, thus it lives rent free in my head to this day.

Tiny64 is a cycle-accurate recreation of the Commodore 64 which can run on targets like the Tufty2040. By disabling some emulated VIC-II features it runs around 20fps, 1/3rd of real time. This is usable for demonstrations.

The C64 is a great choice for emulation. Most projects, including VICE, start by emulating the 6510 CPU then add callbacks to the VIC-II, the CIAs, the disk drives etc. This is good enough to run BASIC. To run more complicated software, especially demos which rely on cycle-accurate timing, executing one CPU instruction, then trying to move the various VIC-II state machines forward is complex and error-prone. Tiny64 chose a different direction.

At the heart of the C64 is the VIC-II chip, which runs a 8 times faster than the 6510 CPU. On each VIC-II clock a pixel is calculated and displayed on the screen. After four dots, the VIC-II executes a memory read where it loads screen data from memory, then another four dots, then the VIC-II performs housekeeping and passes control to the 6510 to execute one cycle, then the cycle repeats.

This does mean that we have to model the internal 6510 state, known as T states, in the emulator; instruction fetch in state T0, operand fetch in state T1, and so on. From the design of an emulator, these CPU states only occur every 8 VIC-II states so the hierarchy of operations; 312 lines per screen, 63 columns per line, 8 VIC-II clocks per column, 2 VIC-II cycles per column, one 6510 cycle per column matches the internal state machine in the VIC-II which is the real powerhouse in the C64; the 6510 CPU simply exists to update VIC-II registers infrequently.

For the Tufty2040 implementation the screen is modelled by a 320×240 16bpp framebuffer which takes up most of the memory in the badge. After each VIC-II frame — 157,248 dots — the framebuffer is copied to the ST7789V via DMA. It takes around 50ms to render a frame on the Tufty2040, and around 1/4 that time to DMA the framebuffer to the display. We avoid the cost of double buffering because writes to the screen run ahead of updates so there is no visible tearing.

Tiny64 also runs at full speed on desktop platforms using SDL3 where it is able to run cycle-accurate demos. With enough patches, on Linux at least, you can build the desktop SDL3 version of Tiny64 with Tiny Go.

The code is available on GitHub, davecheney/tiny64