144 lines
6.3 KiB
Markdown
144 lines
6.3 KiB
Markdown
# C64 Demo — Teletype Games slide show (Oscar64)
|
||
|
||
A Commodore 64 slide show written in C, built with the
|
||
[Oscar64](https://github.com/drmortalwombat/oscar64) compiler. A port of the
|
||
[c64-demo](../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
|
||
|
||
1. **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.
|
||
2. **Our first game** — *DEFINITELY NOT AN IMPOSTOR* in big 3×5 block letters
|
||
with the gold wash, "introduced on TIC-80".
|
||
3. **Our second game** — *IS COMING* in big letters with a drop shadow; raster
|
||
bars run on the border only, so the text stays readable.
|
||
4. **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.
|
||
5. **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
|
||
|
||
```sh
|
||
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 `$C000`
|
||
and 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.
|