Skip to content

video: correct programmable-scan (31 kHz VARBEAMEN) display placement and presentation#247

Merged
LinuxJedi merged 4 commits into
mainfrom
fix/varbeamen-comparator-origin
Jul 21, 2026
Merged

video: correct programmable-scan (31 kHz VARBEAMEN) display placement and presentation#247
LinuxJedi merged 4 commits into
mainfrom
fix/varbeamen-comparator-origin

Conversation

@LinuxJedi

Copy link
Copy Markdown
Owner

What

The Linux/m68k framebuffer console (amifb on an A4000/AGA, following up on #246) was unreadable: every line lost its leading ~22 characters and the picture sat pinned to the left of the display with a black band on the right. amifb programs a display no Amiga software ever exercised before -- a 31 kHz programmable scan (VARBEAMEN, HTOTAL=113) in super-hi-res with FMODE=3, DIW $45 against DDF $18 -- and it flushed out three independent defects in the programmable-scan display path. Three commits:

video: restart the Denise H counter at 0 on programmable scans
Under VARBEAMEN the horizontal comparators were evaluated with the standard 15 kHz counter phase (origin $24 at the line start). The DIW values 31 kHz modes program only make sense against a counter that restarts at 0 with the programmable line: amifb opens its window at $45 against a programmed HSYNC of $0C..$1A -- the documented back porch under a zero origin, but inside the sync pulse under the standard phase -- and the KS3.1 DblPAL screen's DIW $5B agrees (this also accounts for the last ~14 px of its long-standing left-edge offset against the FS-UAE reference). A render-scoped comparator-origin shift now moves every comparator-anchored position (DIW window flip-flop scan, sprite HSTART matches) on programmable frames; beam-anchored positions (fetch slots, register-write landings) are physical line positions and do not move. Standard scans use shift 0 and render byte-identically.

video: scale DIW-relative placement terms for super-hi-res
fetch_origin_native_shift converts DIW H units into native samples with *2/pixel_repeat -- right for lo-res and hi-res, but half the real factor in super-hi-res (four native samples per H unit). The DDF-derived term in the same expression already scaled per resolution, so any SHRES screen whose DIW sits away from the standard fetch reference placed its picture at half the intended offset. Every previously calibrated case was immune (lo-res/hi-res scale correctly; standard-DIW SHRES zeroes the term); amifb's decoupled DIW/DDF put the console 194 native px left of the window and hid the first 24 bytes of every row. The clamped-window correction also measures against the frame's active comparator anchor now instead of the fixed standard one.

video: anchor the programmable-scan presentation at the sync pulse
Programmable frames were presented by mapping the whole HTOTAL line onto the glass from the capture aperture's left edge. A multisync monitor locks its deflection to the sync pulse: when the mode programs its sync generator (VARHSYEN), the glass now shows the line from the HSYNC trailing edge to the next pulse through a windowed row resample, so the picture sits where the mode's own porches place it. Modes without programmed sync keep the previous time-linear map; standard scans are untouched.

The investigation cross-checked WinUAE's model (drawing.cpp's BPLCON1 composition and delayoffset) for the scroll decode and placement semantics, and verified at each step against guest ground truth (the kernel's vc text buffer and a direct bitplane decode of chip RAM, both holding complete lines while the display truncated them).

Verification

  • Full workspace suite (1782 tests) including the 20 probe-golden renders byte-identical; image-regression baselines including the DblPAL programmable-scan structural test (the only failure is the documented ocs_bpu7_ham host-flaky perf test, failing identically on clean main).
  • Inside The Machine (OCS overscan, coupled DIW/DDF): screenshots byte-identical to main at 45 s and 90 s.
  • The Debian 3.1/m68k console is fully readable end to end: complete lines from column 0, porch-set centring with a proper left border, clean debian-uae login: prompt.
  • cargo clippy clean, touched files rustfmt-clean; docs/internals/video.md updated with the counter-origin and presentation model.

Under BEAMCON0 VARBEAMEN the horizontal comparators were evaluated with
the standard 15 kHz counter phase (H_COUNTER_LINE_ORIGIN at the line
start). The DIW values 31 kHz modes program only make sense against a
counter that restarts at 0 with the programmable line: Linux/m68k amifb
opens its display window at $45 against a programmed HSYNC of
$0C..$1A, which is the documented back porch under a zero origin but
would open the window inside the sync pulse under the standard phase -
and the window comparator then matched off the left canvas edge, so the
fb console lost its leading column and sat pinned to the canvas edge
with no border. The KS3.1 DblPAL screen's DIW $5B places its picture
flush under the same zero origin, which accounts for the last ~14 px of
its long-standing left-edge offset against the FS-UAE reference.

A render-scoped comparator-origin shift (set from the frame geometry
like the debug layer masks) now moves every comparator-anchored
position - the DIW window flip-flop scan and sprite HSTART matches - by
H_COUNTER_LINE_ORIGIN on programmable frames. Beam-anchored positions
(fetch slots, register-write landings, colour-write landings) are
physical line positions and do not move. Standard scans use shift 0 and
render byte-identically: the probe-golden suite and the standard
image-regression baselines pass unchanged.

The wide-FMODE SHRES fetch-to-display phase on these modes still shows
a residual (leading fetch gulps of a row are discarded where real
silicon buffers them across the window opening); that calibration is
separate and under investigation.
fetch_origin_native_shift converts DIW H units (lo-res pixels) into
native samples with *2/pixel_repeat, which is right for lo-res (one
sample per unit) and hi-res (two) but half the real factor in
super-hi-res, where a native sample is half a framebuffer pixel and one
H unit spans four samples. The DDF-derived term already used the
correct per-resolution scale, so any screen whose DIW sits away from
the standard fetch reference had its SHRES picture placed at half the
intended offset. Every calibrated case was immune: lo-res and hi-res
scale correctly, and standard-DIW SHRES screens zero the term.

Linux/m68k amifb's 31 kHz console is the first decoupled case: DIW $45
with DDF $18 came out 194 native px left of the window, hiding the
first 24 bytes of every row - the console lost its leading ~22
characters on every line (the BPLCON1 scroll of 19 covers the
difference). With the unit fixed the picture sits flush with the
window and the console is complete.

The clamped-window correction now measures against the frame's active
comparator anchor instead of the fixed standard one, so a programmable
scan whose window is on-canvas no longer gets a spurious off-canvas
clip compensation; standard frames use the same anchor as before.

Verified: full workspace suite (probe-golden renders byte-identical),
image-regression baselines including DblPAL, and Inside The Machine
screenshots byte-identical to main at 45s and 90s.
A programmable (VARBEAMEN) frame was presented by mapping the whole
HTOTAL line onto the glass time-linearly from the capture aperture's
left edge, which pinned the picture toward the left and left the sync
and blanking interval as a black band on the right. A multisync monitor
locks its horizontal deflection to the sync pulse: the visible raster
runs from the HSYNC trailing edge to the next pulse, and the picture
sits wherever the mode's own porches place it.

When the mode programs its sync generator (VARHSYEN with a sane
HSSTRT..HSSTOP), the presentation now resamples that interval onto the
glass through a windowed variant of the row stretch; the source origin
may sit left of the captured aperture, clamping to the border columns.
Modes that leave sync unprogrammed keep the previous time-linear
whole-line map, and standard scans are untouched. The Linux amifb
console gains its proper left border and porch-set centring; the
DblPAL structural regression and the full suite pass unchanged.

Also wraps three overlong lines from the previous two commits that had
escaped rustfmt; docs/internals/video.md gains the programmable-scan
counter-origin and presentation model.
Copilot AI review requested due to automatic review settings July 21, 2026 20:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes multiple issues in Copperline’s programmable-scan (VARBEAMEN / 31 kHz) video path so that unusual ECS/AGA display modes (notably Linux/m68k amifb on A4000/AGA) render with correct horizontal placement and monitor-like presentation, while keeping standard 15 kHz paths byte-identical.

Changes:

  • Adjust comparator-anchored horizontal positioning for programmable scans by introducing an “active canvas shift” and applying it to DIW/sprite comparator mappings.
  • Correct SHRES DIW-relative placement scaling so DIW/DDF decoupling in super-hi-res modes produces the intended offsets.
  • Present programmable scans like a multisync monitor by anchoring the visible interval to the programmed HSYNC pulse (with a fallback to time-linear whole-line mapping when sync isn’t programmed), including a new windowed horizontal resampler.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/video/window/present.rs Passes per-frame presentation window into the common post-processing pipeline.
src/video/window.rs Updates frontend presentation call sites to supply the programmable-scan presentation window.
src/video/present_common.rs Adds programmable-scan sync-anchored presentation path (windowed horizontal resample).
src/video/bitplane/sprite.rs Applies programmable-scan comparator-origin shift to sprite horizontal placement.
src/video/bitplane.rs Implements active comparator-origin shift for programmable scans and fixes SHRES DIW-relative scaling.
src/screenshot.rs Adds stretch_rows_x_window for sync-anchored, windowed horizontal resampling.
src/cpu.rs Applies the sync-anchored programmable-scan presentation in headless/screenshot output.
src/chipset/agnus.rs Exposes the programmed HSYNC window for VARBEAMEN+VARHSYEN modes.
src/bus.rs Computes a programmable-scan presentation window for consumers (renderer/frontend/headless).
src/bin/bench.rs Updates benchmark presentation pipeline to pass the presentation window.
docs/internals/video.md Documents programmable-scan comparator origin and sync-anchored presentation model.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/bus.rs Outdated
The sync-anchored presentation window was computed from the live Agnus
sync latches at present time while the geometry it pairs with is
latched at the frame wrap, so sync registers rewritten after the wrap
could present the previous frame through the next frame's window (a
one-frame horizontal jump in programmable modes). The window is now
computed once at the wrap beside the frame geometry, promoted to the
last-frame slot with it, recomputed after a state load, and carried as
a serde-skipped presentation cache so the save-state layout is
unchanged.

Also passes the new presentation-window argument in the browser
frontend's present call, which the desktop-focused sweep missed and
which broke the wasm32 CI check.
@LinuxJedi
LinuxJedi merged commit 112aedf into main Jul 21, 2026
14 checks passed
@LinuxJedi
LinuxJedi deleted the fix/varbeamen-comparator-origin branch July 21, 2026 20:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants