Astéroric — detailed features
A study clone of Asteroids for the Oric-1 48K · C + 6502 assembly (cc65) · EUPL 1.2 licence
This page lists Astéroric's features with, for each, what it does and how. The GitHub repository (README, ROADMAP, CHANGELOG) remains the up-to-date reference; the manual covers usage.
Contents
1. HIRES vector rendering
Astéroric draws everything — ship, asteroids, saucers, torpedoes, letters, digits — with line segments, like the vector-screen arcade cabinet, but on the Oric's bitmap HIRES screen.
Monochrome HIRES mode
240 × 200 pixels, PAPER 0 / INK 7. The character set is copied from $B400 to $9800 and the three bottom text lines are cleaned. The video attribute set at init is $1E (HIRES 50 Hz, PAL): with $1C (60 Hz) an RGB2HDMI would not lock onto the signal on a real Oric-1.
XOR Bresenham
Every segment is drawn in XOR: drawing it again erases it, with no background save. The draw_line_xor routine (src/asm/line.s) is self-modifying assembly, with a dedicated path for pure verticals and per-byte processing on the horizontal axis. Vertices shared between segments are drawn only once (half-open Bresenham), otherwise the XOR would cancel them.
Pre-rendered sprites
Shapes that do not rotate (asteroids, saucers, digits) are pre-rendered and XOR-blitted instead of being redrawn segment by segment — phase 26, which raised idle CPU time to 39.8%.
Anti-flicker
Erase and redraw of each entity are kept tight in the loop (erase → tick → draw per entity, previous positions remembered) to shorten the window during which an object is missing from the screen.
Sub-pixel motion
Ship and asteroids in 8.8 fixed point: fractional speeds and smooth trajectories despite the 240 × 200 grid. The ship rotates through 32 angles (11.25°) with precomputed tables.
2. Arcade fidelity
The game logic is transposed from the disassembly of the arcade ROM rev 4 (6502disassembly.com): same CPU as the Oric, and the original variable names are kept for traceability.
Authentic asteroids
Four shapes × three sizes, vertices extracted from the ROM (Nick Mikstas's work), 11 to 13 vertices per shape without decimation. Up to six asteroids at once (performance limit).
Waves and fragmentation
Progressive wave spawning with positions and velocities drawn from the arcade's pseudo-random generator; a large asteroid that is hit yields three fragments, the parent shrinks; fragment speed retuned (phase 29) and overall speed reduced (phase 37) after playtesting.
Cylinder and torus
Wrapping from one edge to the other is done by instance duplication (an object straddling the edge is drawn on both sides); collisions are computed in toroidal topology.
Ship, shots, hyperspace
Arcade-faithful 5-segment ship (cockpit bar, notches), thrust flame, 2 × 2 px torpedoes with arcade range and wraparound, rapid fire when SPACE is held, hyperspace with a 25% chance of death. Extra life every 10,000 points, capped at 60,000; at most four life icons displayed.
Explosions
The ship breaks into five debris pieces (rev 4 ShipExpVelTbl/ShipExpPtrTbl tables); asteroids explode into short-lived dots with the arcade's exact shrapnel pattern.
Game over and scores
Three-step sequence (explosion → GAME OVER → HIGH SCORES table), 5 s lock, top-five scores kept in RAM for the session.
3. Saucers
Large and small
Two saucers with firing AI transposed from the arcade: 8-way shots or aiming at the ship, accuracy indexed on the score, the small one alone above 35,000 points.
End-of-wave pressure
ScrSpeedup: the saucer returns faster when only a few asteroids are left; AstBreakTimer blocks its appearance right after an asteroid is hit.
4. AY-3-8912 sound
The original arcade used discrete analogue oscillators; Astéroric's effects are an original recreation on the Oric's PSG (no Atari audio data reused).
Interrupt-driven player
Sound is played by a routine under the VIA Timer 1 IRQ at 50 Hz (every other update at 25 Hz), which frees the main loop. PSG access is protected (sei, dedicated scratch) — two IRQ corruption bugs were found and fixed.
Three independent channels
Channel A: effects (fire, S/M/L explosions, thrust, hyperspace, extra life); channel B: the thump-thump heartbeat that speeds up with the asteroid count; channel C: the saucer's continuous beeping. Hardware envelopes (R11-R13) for natural decays.
Title jingle
On entering the title screen, “In the Hall of the Mountain King” (Grieg, public domain), accelerando, legato, with an octave doubling on channel B.
5. Title screen and display
A-Z vector font
Since phase 40, a complete vector font (src/font.c) renders “ASTERORIC”, “GAME OVER”, “PRESS SPACE”, “HIGH SCORES”, “WAVE” and the CONTROLS screen through a generic text_draw function.
Attract demo
As on the cabinet, asteroids drift behind the title; “PRESS SPACE” blinks; the game really waits for SPACE (no timed start).
HUD
7-segment score digits, life icons, two-digit wave number.
6. Keyboard and joystick
Direct keyboard scan
The keyboard is read through the VIA and PSG register 14, column by column, without the ROM keyboard buffer — the game does not depend on the Atmos ROM $0265. The full 64-position matrix (src/keys_tab.h) is documented from the ROM tables and validated on hardware.
Configurable keys
On the title screen, K opens CONTROLS: LEFT, RIGHT, THRUST, FIRE, HYPER are entered in turn, duplicates and unknown positions refused, ESC cancels and restores. The mapping lives in RAM (defaults on reload).
IJK joystick
The IJK interface is read on the printer port (VIA port A directly, PB4 strobe) with a presence bit: without an interface, the read is ignored. The first protocol (reading through the PSG) reproduced a wrong emulator model and was fixed after feedback on a physical interface; Phosphoric was fixed alongside. Not remappable.
7. Performance
The budget is about 40,000 cycles per frame at 25 Hz on a 1 MHz 6502.
Fixed-step scheduler
Phase 28: fixed step with catch-up, paced by the VIA Timer 1 (20 ms) — no more 60 ms stalls, constant 25 Hz. An adaptive 50 Hz mode was tried and rejected.
Measurements
Initial drawing at ~97 cycles/pixel (phase 2), since reduced by self-modification, byte batching and pre-rendered sprites; idle CPU time in play raised from 17.9% to 39.8% (phases 24-26). make bench profile over 25 million cycles.
Video synchronisation
The CB1 line (VSync) is not wired on a real Oric-1: timing comes from the free-running Timer 1, not from the video signal.
8. Specifications
| Item | Value |
|---|---|
| Target machine | Oric-1 48K, BASIC 1.0 ROM |
| Screen | HIRES 240 × 200 monochrome, 50 Hz (attribute $1E) |
| Frame rate | Constant 25 Hz (VIA Timer 1, 20 ms) |
| Sound | AY-3-8912, 3 channels, IRQ-driven player at 50 Hz |
| Input | Keyboard (direct VIA scan, remappable), IJK joystick (printer port) |
| Loading | asteroric.tap (~23 KB) with autorun flag, code at $0500 |
| Languages | C (cc65) + 6502 assembly (ca65) |
| Licence | EUPL 1.2 (original code); Atari-derived elements not covered |
9. Build and tests
Prerequisites
cc65 ≥ 2.19 (cc65, ca65, ld65), Python 3 for the table generators (ship_verts.s, shapes.s), and Phosphoric to run, capture and profile (path to adjust in the Makefile).
Make targets
make produces build/asteroric.tap; make run / make run-joy launch under Phosphoric (keyboard / emulated IJK); make test captures the screen headless; make check compares bit for bit against the versioned reference capture; make host-test builds and runs the x86 host tests; make bench profiles 25 M cycles.
Tests
Portable host tests on the pseudo-random generator and the shared key table (8 assertions), a title-screen reference capture compared bit for bit, scripted firing scenarios under Phosphoric (--type-keys) for keyboard/joystick non-regression.
10. Known limits
Oric Atmos
The .tap does not work with the Atmos ROM (BASIC 1.1), under Phosphoric or Oricutron. The nominal target remains the Oric-1; Atmos compatibility is a separate sprint.
Non-persistent high scores
The top-five scores and the key mapping live in RAM: everything is lost on reload. Tape/disk saving is on the roadmap.
Six asteroids at most
A limit set to hold 25 Hz; the arcade shows more.
Torpedoes
On real hardware, torpedoes remain hard to see at high speed despite the move to 2 × 2 px.