6.3 KiB
C64 Demo — Teletype Games slide show (Oscar64)
A Commodore 64 slide show written in C, built with the Oscar64 compiler. A port of the c64-demo originally written in 6502 assembly (ACME): five slides rotate automatically with per-slide transitions through black, and the show can also be driven from the keyboard.
The slides
- TTG logo + credits scroller — giant TTG letters built from 8×8 bitmaps with a gold color wash running across them, a pulsing TELETYPE GAMES title, and a smooth pixel scroller with the crew credits.
- Our first game — DEFINITELY NOT AN IMPOSTOR in big 3×5 block letters with the gold wash, "introduced on TIC-80".
- Our second game — IS COMING in big letters with a drop shadow; raster bars run on the border only, so the text stays readable.
- Contact — pulsing big CONTACT title, web / BBS / IRC lines, a 12-cell three-layer parallax starfield, and six hardware sprites (four-pointed stars) floating on sine-wave paths behind the text.
- The assembly confession — a justified-text piece about how this slide show was written with a little help from an AI; the body types in live (one character per frame with a white block cursor), and a closing line appears and pulses when the typing is done.
Each slide is shown for 8 seconds (SLIDE_TIME in src/defs.h, PAL frames);
slide 5 gets 9 seconds (S5_TIME) to let the typewriter finish.
Controls
| Key | Action |
|---|---|
SPACE |
next slide immediately |
INST/DEL (backspace) |
previous slide |
P |
pause toggle — the countdown freezes and a white P marker appears in the top-right corner; effects keep running, and SPACE/DEL still work while paused |
M |
mute/unmute — toggles the SID music on or off |
Keys are read directly from the CIA1 keyboard matrix (the KERNAL IRQ is disabled).
Build and run
make build # compile -> build/demo.prg (builds the compiler too on first run)
make run # build + launch in VICE (requires x64sc)
make rebuildrun # clean rebuild + run
make clean # remove the build directory
make deps # on macOS: install VICE via Homebrew
The same targets are available as VS Code tasks from .vscode/tasks.json
(build is the default build task).
Loading the .prg on a real or emulated C64:
LOAD"*",8,1
RUN
Source layout
| File | Contents |
|---|---|
src/main.c |
main loop, slide sequencer, keyboard scanning (SPACE / DEL / P / M), slide tables |
src/defs.h |
hardware pointers, colors, layout/timing constants, the screen-code charmap |
src/transitions.c |
slide transition engine: show/hide, three transition types (fade, dissolve, wipe), wait_frame raster sync, color snapshot |
src/common.c |
shared routines (clear_screen, print_text, draw_big), shared tables (row×40 lookup, 3×5 block font, wash/pulse/fade color ramps) |
src/music.c |
SID music driver: three-voice pattern player (bass, drums, echo lead) with the note/pattern tables of the Impostor tune |
src/slide1.c … slide5.c |
one file per slide: sN_init (draws the screen), sN_update (runs once per frame), plus slide-local data |
The translation units are tied together by Oscar64's
#pragma compile("file.c") mechanism: every header pulls in its matching
.c file, so make only needs to pass src/main.c to the compiler.
How it works
Slide framework
Every slide consists of an init routine (draws the whole screen), an
update routine (called once per frame, runs exactly one frame) and a
duration in frames. The main loop in main.c calls the current update, counts
the frames down, and steps to the next slide when time runs out — or
immediately on SPACE/DEL.
Timing
There are no interrupts: the code polls the raster register ($D012,
vic.raster) and does all screen updates while the beam is in the lower border
(RAS_BOTTOM). Slide 1 additionally splits the frame mid-screen to enable
horizontal fine scrolling (vic.ctrl2) for the scroller row only; slide 3
chases the beam down the frame, recoloring the border every 4 lines to draw
the raster bars.
Transitions
Each slide has its own in-transition and out-transition, chosen from three
types via tin_tbl / tout_tbl in main.c:
- Fade — steps every color RAM cell through
fadetbl(each color → one shade darker; every chain hits black within 4 steps). The new slide is drawn with the display off, its colors are snapshotted to the buffer at$C000and blanked, then the fade-in walks each cell back up to its snapshot color. - Dissolve — a 10-bit Galois LFSR (period 1023) visits every screen cell exactly once in pseudo-random order. 25 cells per frame = 40-frame (~0.8 s) full reveal.
- Wipe — column-by-column sweep from left to right, one column per frame.
All transitions start with a short white-to-grey border flash.
Music
src/music.c is a three-voice SID pattern player; the tune is track 0
("room work") of the Impostor TIC-80 cart, converted by hand into a 128-row
pattern grid (bass: 50% pulse, drums: noise, echo canon: decaying triangle
pluck 3 octaves up, delayed by 2 rows). Rows are one byte per voice:
$00 = nothing, $01 = key off, $80|n = play note n. music_play is
ticked once per frame: from the main loop and from wait_frame, so the music
keeps playing through transitions.
Memory map
$0801 .. ... BASIC stub + machine code (Oscar64 defaults)
$0340 .. $037E sprite shape (cassette buffer, 64-aligned)
$0400 .. $07E7 screen RAM (written directly)
$C000 .. $C3E7 color RAM snapshot buffer for transitions
$D000 .. $D02E VIC-II registers (display, sprites, raster)
$D400 .. $D418 SID registers (music)
$D800 .. color RAM
$DC00 / $DC01 CIA1 keyboard matrix
Differences from the original
- Texts are converted to screen codes at compile time via
#pragma charmap(97, 1, 26), so plain C string literals can be written straight to the screen. - Self-modifying vectors and zero-page pointers are replaced by function pointers and ordinary C array indexing; the behavior (timing, effects, music) is unchanged.
- Compiled with
-n -O2(fully native 6502 code) so the raster-polling parts are fast enough to fit in the frame.