The picture and the ULA
Phosphoric’s ULA no longer draws a line in one go: it reads one cell per cycle, at the instant the beam crosses it. That is what makes the mid-line effects of demos possible.
On this page
The scan, in cycles
The ULA’s horizontal counter counts from 0 to 63 on each line, one count per cycle:
- screen columns 0 to 39 are read at counts 0 to 39, one 6-pixel cell per cycle, giving 240 pixels;
- blanking takes counts 40 to 63, and the sync pulse counts 49 to 52.
“Column 0 on cycle 0 of the line” is therefore not a convention of the emulator: it is the chip’s
counter, as an unofficial ULA guide recorded with an oscilloscope. The --ula-fetch-offset option
remains for experiments, but its right value is 0.
A frame has 312 lines, 224 of them visible: 200 lines of text or HIRES, then 3 rows of text. 312 × 64 = 19,968 cycles per frame, 50 frames per second.
One cell per cycle
Since version 2.0, Phosphoric’s ULA reads each cell on the cycle when the beam reads it. Ink, paper and text attributes are a line state, which persists from one cell to the next, as on the chip.
Consequence: a processor write in the middle of a line only reaches the cells not yet scanned. A program that changes the colour or the mode mid-line gets what the hardware would give, not a uniform line. That is the raster split.


--ula-line, the rendering from before 2.0: each line read in one go,
boundaries in steps.The proof
make test-raster-splitruns a real 32-byte 6502 program that rewrites screen memory, alternating two background colours. It compares per-cycle rendering with line-by-line rendering, and requires lines that differ over only part of their width: a cut in the middle of a line, which line-by-line rendering cannot produce.make test-clockholds the deterministic, exact case: a write at cycle c is seen by cell c and the following ones.
The modes
| Mode | Resolution | Memory |
|---|---|---|
| Text | 40 × 28 characters of 6 × 8 pixels | screen at $BB80, characters at $B400 and $B800 |
| HIRES | 240 × 200 pixels, 6 pixels per byte | screen at $A000 (40 bytes per line), then 3 rows of text |
| Double height, blinking, alternate set | text attributes 8 to 15 | — |
In a HIRES byte, the six low bits are the pixels; if bits 5 and 6 are both zero, the byte is an
attribute. A mode attribute (24 to 31) can change mode mid-frame: the read source then switches
between $A000 and $BB80, line by line. Bit 7 of a byte inverts its cell: it complements ink and
paper (XOR 7), it does not swap them.
What is not drawn
- The border and blanking: the picture is always 240 × 224 pixels.
- The absolute phase between the scan and the processor: the ULA’s counters run freely and the 6502’s reset is asynchronous, so no software can observe it on an unmodified Oric. Only a wire added from the ULA to the VIA, the “VSYNC hack”, would make it visible; it is not emulated.
- The ULA-NG’s extended modes in full screen are rendered per line, not per cell.
Display and export
| Output | Option |
|---|---|
| Window enlarged ×1 to ×4 (240 × 224 to 960 × 896), without smoothing | --scale N, F3 key |
| Full screen | F11 |
| PNG screenshot | F12; --screenshot FILE at the end, --screenshot-at CYCLES:FILE |
| Screenshot when a memory location takes a value | --screenshot-when ADDR:VAL:FILE |
| Screen text in ASCII, or picture as ANSI text | --screenshot-text, --screenshot-ansi |
| Motion-JPEG video in AVI | --video FILE, --video-fps, --video-quality |
| Streaming to a Chromecast (720 × 672 MJPEG and WAV sound) | --cast-server, --cast-to (CAST=1 build) |
Cycle-triggered screenshots are repeatable: --screenshot-at can be given several times in the same
run. That is how a series of pictures of a demo is taken.