Skip to content
Phosphoricwiki
Contents

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

One PAL frame of the ULA, in cyclesA line lasts 64 counts of the ULA's horizontal counter. 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 sync counts 49 to 52. 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.Horizontal counter: 64 counts per line, 1 count = 1 cycle = 1 µslines 0-199text or HIRES · 40 cells × 6 pixelslines 200-223: 3 rows of textblankingsyncblankinglines 224-311: outside the picture0404952630200224311frame lines (PAL)Columns 0-39read at counts 0-39Counts 40-63blanking; sync at 49-52224 visible linesout of 312 lines per frame312 × 64 = 19,968 cycles per frame · 50 frames per second
A PAL frame: 312 lines of 64 cycles. Columns 0 to 39 are read at counts 0 to 39.

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.

Black and cyan bands whose boundaries slant across the middle of lines
The test program rewrites the screen while the beam scans it: the boundaries slant across the middle of lines.
Black and cyan bands whose boundaries form steps
The same program with --ula-line, the rendering from before 2.0: each line read in one go, boundaries in steps.

The proof

  • make test-raster-split runs 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-clock holds the deterministic, exact case: a write at cycle c is seen by cell c and the following ones.

The modes

ModeResolutionMemory
Text40 × 28 characters of 6 × 8 pixelsscreen at $BB80, characters at $B400 and $B800
HIRES240 × 200 pixels, 6 pixels per bytescreen at $A000 (40 bytes per line), then 3 rows of text
Double height, blinking, alternate settext 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

OutputOption
Window enlarged ×1 to ×4 (240 × 224 to 960 × 896), without smoothing--scale N, F3 key
Full screenF11
PNG screenshotF12; --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.

Sources in the repository : docs/ACCURACY.md, docs/architecture/master-clock.md, README.md, tests/integration/test_raster_split.sh.