Skip to content

Denise: horizontal DIW comparator flip-flop model#103

Merged
LinuxJedi merged 2 commits into
mainfrom
fix/denise-h-window-flipflop
Jul 4, 2026
Merged

Denise: horizontal DIW comparator flip-flop model#103
LinuxJedi merged 2 commits into
mainfrom
fix/denise-h-window-flipflop

Conversation

@LinuxJedi

Copy link
Copy Markdown
Owner

What

Replaces the span-based horizontal display-window clip with the hardware comparator model (vAmiga Denise::updateBorderBuffer, hardware-verified): one window flip-flop, SET on an exact 9-bit HSTART counter match, CLEARED on an exact HSTOP match, evaluated per lores position with the register values current at that position. No match = no change, so degenerate windows never open or never close, and the state carries across lines and frames.

Hardware details modelled:

  • The counter starts each line at the hblank edge (lores 0x24) and wraps 0x1C8 -> 2 near the line end, so positions 2..0x23 are a line's tail, not its head.
  • OCS Denise does not reset the counter on lines 0-8: it free-runs modulo 0x200, letting otherwise-unreachable stop values (0x1C8..0x1FF, i.e. any OCS stop byte >= 0xC8) fire during vertical blank.
  • Vertical sync leaves the flip-flop set.
  • DIWSTRT/DIWSTOP writes reach the comparators one colour clock after the write cycle.

Consumers converted: background border split, a per-pixel window gate in the playfield painter (replacing the display_window_x span pair and its stop <= start -> +0x100 heuristics at that site), and a post-compositing mask that repaints closed intervals as border (keeps sprites out of closed regions; border sprites exempt). The painter's shifter-origin anchor stays paired with the DIWSTRT value whose comparator fired, so mid-line rewrites move where the window opens without moving the fetched picture.

Also fixes a palette bleed the diw tests exposed: a colour write in the horizontal-blank tail attributes to the previous output row, behind same-beam-line events that already seeded the next row's base palette - the written entry is now patched into that base. Previously a one-line COLOR00 marker set during hblank (vAmigaTS diw1 block 1) vanished entirely.

Test updates

Three unit tests encoded artifacts of the old span clip and now assert comparator behaviour:

  • a later-HSTART rewrite no longer re-clips an already-open window (the flip-flop stays set),
  • a window edge sits at the new HSTART's comparator match, not at the write position,
  • an OCS stop byte >= 0xC8 never closes, so the BRDRBLNK test uses a closing stop.

New unit test covers cross-line carry (unreachable HSTART + late stop rewrite).

Results (vAmigaTS, % pixel divergence)

case before after
Agnus/DIW/OLDDIW/diw10 28.3 9.0
Agnus/DIW/OLDDIW/diw9 65.5 60.0
Agnus/DIW/OLDDIW/diw8 57.1 54.7
Agnus/DIW/OLDDIW/diw1 63.5 60.7
Agnus/DIW/OLDDIW/diw2 50.3 48.0
Denise/DIW/DIWH/diwtim0 5.5 4.9
Denise/DIW/DIWH/diwtim3b 16.3 14.6
Denise/DIW/DIWH/minmax 9.8 9.3

Agnus/DIW/DIWV improves slightly across the board (one +0.3 on onoff1).

The dominant residual in the OLDDIW bucket is not the window model: the whole bucket runs DDFSTRT $30, and Copperline places that early-DDF picture 2 lores pixels left of vAmiga (a uniform shift; with it factored out diw1 measures 7.9%). That is the Agnus/DDF fetch-placement class (oldhwstop* cases at 50-65%) and is tracked separately.

Verification

  • cargo test --release: 1266 + 30 + 4 pass.
  • cargo clippy / cargo fmt --check: clean.
  • image_regression --ignored: 7 pass + the documented host-flaky ocs_bpu7_ham (fails on clean main too).
  • Byte-identity vs main (raw screenshots): KS3.1/A1200 boot, Zool, Gen-X, Inside The Machine, Hamazing - all identical.

LinuxJedi added 2 commits July 3, 2026 22:23
Replace the span-based horizontal display-window clip with the hardware
comparator model (vAmiga Denise::updateBorderBuffer, hardware-verified):
Denise keeps one window flip-flop that is SET on an exact 9-bit HSTART
counter match and CLEARED on an exact HSTOP match, evaluated per lores
position with the register values current at that position. No match
means no change, so degenerate windows never open or never close and the
state carries across lines and frames.

The per-line scan follows the hardware counter: it starts each line at
the hblank edge (lores 0x24), wraps 0x1C8 -> 2 near the line end (so
positions 2..0x23 are a line's tail, not its head), and on OCS lines 0-8
free-runs modulo 0x200, which lets otherwise-unreachable stop values
(0x1C8..0x1FF) fire during vertical blank. Vertical sync leaves the
flip-flop set. DIWSTRT/DIWSTOP writes reach the comparators one colour
clock after the write cycle.

Per-row open spans now drive the background border split, a per-pixel
window gate in the playfield painter (replacing the display_window_x
span pair), and a post-compositing mask that repaints closed intervals
as border (keeping sprites out of closed regions; border sprites
exempt). The painter's shifter-origin anchor stays paired with the
DIWSTRT value whose comparator fired, so mid-line rewrites move where
the window opens without moving the fetched picture.

Also fix a palette bleed the diw tests exposed: a colour write in the
horizontal-blank tail attributes to the previous output row, behind
same-beam-line events that already seeded the next row's base palette;
the written entry is now patched into that base so a one-line COLOR00
marker set during hblank still paints its row.

Three unit tests encoded artifacts of the old span clip (re-clipping an
already-open window on a later-HSTART rewrite, a window edge at the
write position instead of the comparator match, and a never-closing OCS
stop byte >= 0xC8 treated as closing); they now assert the comparator
behaviour.

vAmigaTS Agnus/DIW/OLDDIW: diw10 28.3% -> 9.0%, diw9 65.5% -> 60.0%,
diw1 63.5% -> 60.7%, diw8 57.1% -> 54.7%, diw2 50.3% -> 48.0%; diw1's
remaining divergence is a single uniform 2-lores-pixel picture offset
(the Agnus/DDF fetch-placement class, tracked separately) - with that
shift factored out diw1 measures 7.9%. Denise/DIW/DIWH and Agnus/DIW/
DIWV also improve across the board. KS3.1/A1200 boot, Zool, Gen-X,
Inside The Machine, and Hamazing screenshots are byte-identical to
main.
@LinuxJedi LinuxJedi merged commit a37c834 into main Jul 4, 2026
8 checks passed
@LinuxJedi LinuxJedi deleted the fix/denise-h-window-flipflop branch July 4, 2026 05:35
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.

1 participant