Draw a sight line, a circular scanner or a filled oval without building a renderer first. These are five callable assembly routines over the existing pixel/span interface. You choose the bitmap, own the memory and call only the pieces your game needs.

Include it and make a call
BP at $7000 and G at $7200 in the complete caller. Include the existing pixel_addr helper after the backend. Attributes at $5800–$5AFF are separate: these routines change bitmap bits, not cell colours. The bitmap uses the Spectrum’s interleaved scanline address; incrementing Y is not adding 32 to an address.
Define GEO_WIDTH and GEO_HEIGHT as 256 and 192. Set GEO_PEN_MAX to 255 (pen ignored). Include the geometry z80.inc and the backend exactly once, as shown at the bottom of the complete caller. Reserve 160 bytes at G plus 48 at BP, separately from code, stack, tables and display. Scratch needs no initialisation; public inputs do.
After the caller’s video setup, this is an ordinary direct call. Your game does not need the verification mailbox that follows it.
Outline an ellipse centred at (80,70), radii (48,24)
; A normal game call: an outlined ellipse. Setup and mailbox are separate.
ld hl,80
ld (G+0),hl
ld hl,70
ld (G+2),hl
ld hl,48
ld (G+4),hl
ld hl,24
ld (G+6),hl
ld hl,0
ld (G+12),hl
ld hl,0
ld (G+14),hl
ld hl,256
ld (G+16),hl
ld hl,192
ld (G+18),hl
ld a,0
ld (G+8),a
ld a,0
ld (G+9),a
ld a,1
ld (G+11),a
call geo_ellipse
ld a,0
ld (COMMAND),a
ld a,239
ld (COMMAND+1),aRead G+10 after return: 1 means accepted, 0 means rejected. An accepted shape can be completely outside the scissor and draw nothing. Change the inputs and call again; G holds no hidden scene or list of shapes.
The calling contract
All words use little-endian byte order. Screen origin is top left; Y increases downwards. Pen is ignored by this monochrome backend; initialise it to 1 for a caller that also ports to CPC.
| G offset | Supply or read |
|---|---|
| 0,2 | Signed x0,y0 for a line; centre cx,cy for curves |
| 4,6 | Signed x1,y1 for a line; radii rx,ry for an ellipse; a circle uses rx and ignores ry |
| 8 | Operation: 0 set/replace, 1 clear, 2 XOR |
| 9 | Line: 0 includes both ends, 1 omits the original caller’s final end; curves require 0 |
| 10 | Output: 1 accepted, 0 rejected |
| 11 | Pen index (ignored on mono backends) |
| 12,14,16,18 | Unsigned scissor left,top,right,bottom; right/bottom exclusive |
| 20 | Output: 32-bit destination-byte writes |
| 24 | Output: 16-bit backend calls |
| 26–159 | Private scratch |
Call geo_line, geo_circle, geo_ellipse, geo_fill_circle or geo_fill_ellipse. Coordinates must be −512..511, radii 0..255. The scissor must be nonempty and inside the 256×192 display. Invalid bounds, radii, operation, flags or pen reject before any destination write, with zero counters. Inputs at 0–9 and 11–19 survive. Unused circle ry is ignored. A zero radius becomes a point or straight span rather than an error.
Keep the line you asked for
The line uses accumulated integer error in all eight octants. It orders endpoints consistently before choosing half-way pixels, so reversing a line produces the same pixels. A horizontal or vertical line uses a span fast path. A zero-length inclusive line draws one pixel.
For a closed XOR polyline, give every edge flag 1. Each omits its original final endpoint, so the next edge owns that shared vertex. Leaving both endpoints on both edges would toggle the vertex twice and erase it. A zero-length omit-final line draws nothing.
The scissor discards outside pixels from the original raster, preserving its rounding phase. It does not replace the endpoints with rounded intersections. Offscreen steps still cost work: the accepted coordinate range bounds the major-axis delta at 1023. Filled curves instead intersect each row span with the scissor before calling the backend.
Draw with integer error works through a short line, symmetry, span filling and overflow. Place a point explains the origin difference; for integer screen rows here, convert an upwards world Y with screenY=height-1-worldY.
Fill rows, not thousands of point calls
Outline ellipses update an integer decision and reflect points into four quadrants. Axis points are emitted once. Filled ellipses select the widest interval for each row and emit that span once; they do not repeatedly XOR the same row as the edge advances. Circles use equal ellipse radii through this same kernel. A specialised eight-way circle implementation could reduce work further.
An ellipse with radii (37,15) covers 1,833 logical pixels here but needs only 31 span calls when filled. Those calls made 258 byte writes on this backend. That is actual destination work, not elapsed cycles. Four-pixel CPC packing and eight-pixel mono packing produce different counts, and partial edge bytes still need protection.
The geometry occupies 5,110 assembled bytes, the backend 533, plus the 208 RAM bytes for G and BP. The existing pixel_addr helper is additional. Reset code, stack, display and assets are additional. The checked 32-bit curve arithmetic buys a clear range contract but has a code/time cost. Cache static shapes or specialise a hot path when measurement shows a need. No frame-budget claim has been measured here.
A logical circle has equal X/Y radii. Its displayed shape still depends on the display mode, pixel aspect and capture presentation. Choose the presentation deliberately; changing radius to compensate is a separate mapping decision, not a change to the circle equation.
Own the resources
Clobbered: AF/BC/DE/HL and flags. Reserve a conservative 64-byte stack budget, plus your other calls and interrupts. The component is non-re-entrant. It does not change interrupt state or negotiate display access. Keep G/BP private and coordinate another renderer or interrupt user yourself. Reset fixtures own the machine; integration under a live OS needs its own loading and coexistence checks.
Open the complete reusable Z80 geometry include
Include this CPU component once
; SPDX-License-Identifier: MIT
; Copyright (c) 2020 zingl; assembly adaptation Code198x (2026).
; Integer geometry over the existing bp_pixel/bp_hspan/bp_vspan backend.
; G is 160 caller-owned RAM bytes. BP is a separate 48-byte workspace.
; Read README.md for signed inputs, scissor, endpoint flags and ownership.
; Bresenham/ellipse recurrences adapted from Alois Zingl; see LICENSE-Zingl.txt.
g_a equ G+0
g_b equ G+2
g_c equ G+4
g_d equ G+6
g_op equ G+8
g_flags equ G+9
g_status equ G+10
g_pen equ G+11
g_clip_l equ G+12
g_clip_t equ G+14
g_clip_r equ G+16
g_clip_b equ G+18
g_writes equ G+20
g_calls equ G+24
g_mode equ G+32
g_first equ G+33
g_swap equ G+34
g_fill equ G+35
g_x equ G+40
g_y equ G+42
g_end_x equ G+44
g_end_y equ G+46
g_dx equ G+48
g_dy equ G+50
g_sy equ G+54
g_err equ G+56
g_e2 equ G+58
g_temp equ G+60
g_last_y equ G+62
g_rx equ G+64
g_ry equ G+66
g_px equ G+68
g_py equ G+70
g_right equ G+72
g_bottom equ G+74
g_a2 equ G+80
g_b2 equ G+84
g_curve_dx equ G+88
g_curve_dy equ G+92
g_curve_err equ G+96
g_curve_e2 equ G+100
g_step_x equ G+104
g_step_y equ G+108
g_work equ G+112
g_mul equ G+158
g_cmp equ G+159
bp_writes equ BP+40
bp_status equ BP+9
bp_op equ BP+8
bp_pen equ BP+11
bp_x0 equ BP+0
bp_y0 equ BP+2
bp_x1 equ BP+4
bp_y1 equ BP+6
geo_line:
ld a,$00
ld (g_mode),a
jp geo_enter
geo_circle:
ld a,$01
ld (g_mode),a
jp geo_enter
geo_ellipse:
ld a,$02
ld (g_mode),a
jp geo_enter
geo_fill_circle:
ld a,$03
ld (g_mode),a
jp geo_enter
geo_fill_ellipse:
ld a,$04
ld (g_mode),a
jp geo_enter
geo_enter:
ld a,$00
ld (g_status),a
ld a,$00
ld (g_writes),a
ld a,$00
ld (g_writes+1),a
ld a,$00
ld (g_writes+2),a
ld a,$00
ld (g_writes+3),a
ld hl,$0000
ld (g_calls),hl
; Validate the entire request before the first backend write.
ld a,$02
ld (g_cmp),a
ld a,(g_op)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_1
jp nz,geo_done
geo_branch_1:
ld a,(g_pen)
cp GEO_PEN_MAX
jp c,geo_branch_2
jp z,geo_branch_2
jp geo_done
geo_branch_2:
ld a,$fe
xor $80
ld (g_cmp),a
ld a,(g_a+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
jp nz,geo_branch_3
ld a,$00
ld (g_cmp),a
ld a,(g_a)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
geo_branch_3:
ld a,$01
xor $80
ld (g_cmp),a
ld a,(g_a+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_4
jp nz,geo_done
ld a,$ff
ld (g_cmp),a
ld a,(g_a)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_4
jp nz,geo_done
geo_branch_4:
ld a,$fe
xor $80
ld (g_cmp),a
ld a,(g_b+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
jp nz,geo_branch_5
ld a,$00
ld (g_cmp),a
ld a,(g_b)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
geo_branch_5:
ld a,$01
xor $80
ld (g_cmp),a
ld a,(g_b+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_6
jp nz,geo_done
ld a,$ff
ld (g_cmp),a
ld a,(g_b)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_6
jp nz,geo_done
geo_branch_6:
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_clip_l+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
jp nz,geo_branch_7
ld a,$00
ld (g_cmp),a
ld a,(g_clip_l)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
geo_branch_7:
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_clip_t+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
jp nz,geo_branch_8
ld a,$00
ld (g_cmp),a
ld a,(g_clip_t)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
geo_branch_8:
ld a,(g_clip_r+1)
xor $80
ld (g_cmp),a
ld a,(g_clip_l+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_9
jp nz,geo_done
ld a,(g_clip_r)
ld (g_cmp),a
ld a,(g_clip_l)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_9
jp geo_done
geo_branch_9:
ld a,(g_clip_b+1)
xor $80
ld (g_cmp),a
ld a,(g_clip_t+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_10
jp nz,geo_done
ld a,(g_clip_b)
ld (g_cmp),a
ld a,(g_clip_t)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_10
jp geo_done
geo_branch_10:
ld hl,GEO_WIDTH
ld (g_temp),hl
ld a,(g_temp+1)
xor $80
ld (g_cmp),a
ld a,(g_clip_r+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_11
jp nz,geo_done
ld a,(g_temp)
ld (g_cmp),a
ld a,(g_clip_r)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_11
jp nz,geo_done
geo_branch_11:
ld hl,GEO_HEIGHT
ld (g_temp),hl
ld a,(g_temp+1)
xor $80
ld (g_cmp),a
ld a,(g_clip_b+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_12
jp nz,geo_done
ld a,(g_temp)
ld (g_cmp),a
ld a,(g_clip_b)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_12
jp nz,geo_done
geo_branch_12:
ld a,$00
ld (g_cmp),a
ld a,(g_mode)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_curve_validate
geo_branch_13:
ld a,$01
ld (g_cmp),a
ld a,(g_flags)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_14
jp nz,geo_done
geo_branch_14:
ld a,$fe
xor $80
ld (g_cmp),a
ld a,(g_c+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
jp nz,geo_branch_15
ld a,$00
ld (g_cmp),a
ld a,(g_c)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
geo_branch_15:
ld a,$01
xor $80
ld (g_cmp),a
ld a,(g_c+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_16
jp nz,geo_done
ld a,$ff
ld (g_cmp),a
ld a,(g_c)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_16
jp nz,geo_done
geo_branch_16:
ld a,$fe
xor $80
ld (g_cmp),a
ld a,(g_d+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
jp nz,geo_branch_17
ld a,$00
ld (g_cmp),a
ld a,(g_d)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
geo_branch_17:
ld a,$01
xor $80
ld (g_cmp),a
ld a,(g_d+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_18
jp nz,geo_done
ld a,$ff
ld (g_cmp),a
ld a,(g_d)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_18
jp nz,geo_done
geo_branch_18:
ld a,$01
ld (g_status),a
jp geo_line_start
geo_curve_validate:
ld a,$00
ld (g_cmp),a
ld a,(g_flags)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_done
geo_branch_19:
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_c+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
jp nz,geo_branch_20
ld a,$00
ld (g_cmp),a
ld a,(g_c)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
geo_branch_20:
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_c+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_21
jp nz,geo_done
ld a,$ff
ld (g_cmp),a
ld a,(g_c)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_21
jp nz,geo_done
geo_branch_21:
ld hl,(g_c)
ld (g_rx),hl
ld a,$01
ld (g_cmp),a
ld a,(g_mode)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_22
jp geo_equal_axes
geo_branch_22:
ld a,$03
ld (g_cmp),a
ld a,(g_mode)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_23
jp geo_equal_axes
geo_branch_23:
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_d+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
jp nz,geo_branch_24
ld a,$00
ld (g_cmp),a
ld a,(g_d)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_done
geo_branch_24:
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_d+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_25
jp nz,geo_done
ld a,$ff
ld (g_cmp),a
ld a,(g_d)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_25
jp nz,geo_done
geo_branch_25:
ld hl,(g_d)
ld (g_ry),hl
jp geo_axes_ready
geo_equal_axes:
ld hl,(g_c)
ld (g_ry),hl
geo_axes_ready:
ld a,$01
ld (g_status),a
ld a,$00
ld (g_fill),a
ld a,$03
ld (g_cmp),a
ld a,(g_mode)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_curve_start
geo_branch_26:
ld a,$01
ld (g_fill),a
jp geo_curve_start
geo_done:
ret
geo_line_start:
; Canonical ordering gives the same tie pixels when endpoints are reversed.
ld hl,(g_a)
ld (g_x),hl
ld hl,(g_b)
ld (g_y),hl
ld hl,(g_c)
ld (g_end_x),hl
ld hl,(g_d)
ld (g_end_y),hl
ld a,$00
ld (g_swap),a
ld a,(g_end_x+1)
xor $80
ld (g_cmp),a
ld a,(g_x+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_27
jp nz,geo_swap_endpoints
ld a,(g_end_x)
ld (g_cmp),a
ld a,(g_x)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_27
jp nz,geo_swap_endpoints
geo_branch_27:
ld a,(g_end_x+1)
xor $80
ld (g_cmp),a
ld a,(g_x+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_line_ordered
ld a,(g_end_x)
ld (g_cmp),a
ld a,(g_x)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_line_ordered
geo_branch_28:
ld a,(g_end_y+1)
xor $80
ld (g_cmp),a
ld a,(g_y+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_line_ordered
jp nz,geo_branch_29
ld a,(g_end_y)
ld (g_cmp),a
ld a,(g_y)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_line_ordered
jp z,geo_line_ordered
geo_branch_29:
geo_swap_endpoints:
ld hl,(g_x)
ld (g_temp),hl
ld hl,(g_end_x)
ld (g_x),hl
ld hl,(g_temp)
ld (g_end_x),hl
ld hl,(g_y)
ld (g_temp),hl
ld hl,(g_end_y)
ld (g_y),hl
ld hl,(g_temp)
ld (g_end_y),hl
ld a,$01
ld (g_swap),a
geo_line_ordered:
ld a,(g_end_y+1)
xor $80
ld (g_cmp),a
ld a,(g_y+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_30
ld a,(g_end_y)
ld (g_cmp),a
ld a,(g_y)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_30
jp geo_horizontal
geo_branch_30:
ld a,(g_end_x+1)
xor $80
ld (g_cmp),a
ld a,(g_x+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_31
ld a,(g_end_x)
ld (g_cmp),a
ld a,(g_x)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_31
jp geo_vertical_line
geo_branch_31:
ld hl,(g_end_x)
ld (g_dx),hl
ld hl,(g_dx)
ld de,(g_x)
or a
sbc hl,de
ld (g_dx),hl
ld hl,(g_end_y)
ld (g_dy),hl
ld hl,(g_dy)
ld de,(g_y)
or a
sbc hl,de
ld (g_dy),hl
ld hl,$0001
ld (g_sy),hl
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_dy+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_33
jp nz,geo_branch_32
ld a,$00
ld (g_cmp),a
ld a,(g_dy)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_33
jp geo_branch_32
geo_branch_33:
ld hl,$ffff
ld (g_sy),hl
ld a,(g_dy)
xor $ff
ld (g_dy),a
ld a,(g_dy+1)
xor $ff
ld (g_dy+1),a
ld hl,(g_dy)
ld de,$0001
add hl,de
ld (g_dy),hl
geo_branch_32:
ld a,(g_dy)
xor $ff
ld (g_dy),a
ld a,(g_dy+1)
xor $ff
ld (g_dy+1),a
ld hl,(g_dy)
ld de,$0001
add hl,de
ld (g_dy),hl
ld hl,(g_dx)
ld (g_err),hl
ld hl,(g_err)
ld de,(g_dy)
add hl,de
ld (g_err),hl
ld a,$01
ld (g_first),a
geo_line_loop:
ld a,$00
ld (g_cmp),a
ld a,(g_flags)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_36
jp geo_branch_34
geo_branch_36:
ld a,$00
ld (g_cmp),a
ld a,(g_swap)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_38
jp geo_branch_37
geo_branch_38:
ld a,$00
ld (g_cmp),a
ld a,(g_first)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_35
geo_branch_39:
jp geo_branch_34
geo_branch_37:
ld a,(g_end_x+1)
xor $80
ld (g_cmp),a
ld a,(g_x+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_34
ld a,(g_end_x)
ld (g_cmp),a
ld a,(g_x)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_34
geo_branch_40:
ld a,(g_end_y+1)
xor $80
ld (g_cmp),a
ld a,(g_y+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_41
ld a,(g_end_y)
ld (g_cmp),a
ld a,(g_y)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_41
jp geo_done
geo_branch_41:
geo_branch_34:
ld hl,(g_x)
ld (g_px),hl
ld hl,(g_y)
ld (g_py),hl
call geo_point
geo_branch_35:
ld a,$00
ld (g_first),a
ld a,(g_end_x+1)
xor $80
ld (g_cmp),a
ld a,(g_x+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_42
ld a,(g_end_x)
ld (g_cmp),a
ld a,(g_x)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_42
geo_branch_43:
ld a,(g_end_y+1)
xor $80
ld (g_cmp),a
ld a,(g_y+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_44
ld a,(g_end_y)
ld (g_cmp),a
ld a,(g_y)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_44
jp geo_done
geo_branch_44:
geo_branch_42:
ld hl,(g_err)
ld (g_e2),hl
ld hl,g_e2
sla (hl)
inc hl
rl (hl)
ld a,(g_dy+1)
xor $80
ld (g_cmp),a
ld a,(g_e2+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_45
jp nz,geo_branch_46
ld a,(g_dy)
ld (g_cmp),a
ld a,(g_e2)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_45
geo_branch_46:
ld hl,(g_err)
ld de,(g_dy)
add hl,de
ld (g_err),hl
ld hl,(g_x)
ld de,$0001
add hl,de
ld (g_x),hl
geo_branch_45:
ld a,(g_dx+1)
xor $80
ld (g_cmp),a
ld a,(g_e2+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_48
jp nz,geo_branch_47
ld a,(g_dx)
ld (g_cmp),a
ld a,(g_e2)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_48
jp nz,geo_branch_47
geo_branch_48:
ld hl,(g_err)
ld de,(g_dx)
add hl,de
ld (g_err),hl
ld hl,(g_y)
ld de,(g_sy)
add hl,de
ld (g_y),hl
geo_branch_47:
jp geo_line_loop
geo_horizontal:
ld a,$00
ld (g_cmp),a
ld a,(g_flags)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_50
jp geo_branch_49
geo_branch_50:
ld a,$00
ld (g_cmp),a
ld a,(g_swap)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_52
jp geo_branch_51
geo_branch_52:
ld hl,(g_x)
ld de,$0001
add hl,de
ld (g_x),hl
jp geo_branch_49
geo_branch_51:
ld hl,(g_end_x)
ld de,$ffff
add hl,de
ld (g_end_x),hl
geo_branch_49:
ld a,(g_end_x+1)
xor $80
ld (g_cmp),a
ld a,(g_x+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_53
jp nz,geo_done
ld a,(g_end_x)
ld (g_cmp),a
ld a,(g_x)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_53
jp nz,geo_done
geo_branch_53:
ld hl,(g_x)
ld (g_px),hl
ld hl,(g_end_x)
ld (g_right),hl
ld hl,(g_y)
ld (g_py),hl
call geo_span
jp geo_done
geo_vertical_line:
ld a,$00
ld (g_cmp),a
ld a,(g_flags)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_55
jp geo_branch_54
geo_branch_55:
ld a,$00
ld (g_cmp),a
ld a,(g_swap)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_57
jp geo_branch_56
geo_branch_57:
ld hl,(g_y)
ld de,$0001
add hl,de
ld (g_y),hl
jp geo_branch_54
geo_branch_56:
ld hl,(g_end_y)
ld de,$ffff
add hl,de
ld (g_end_y),hl
geo_branch_54:
ld a,(g_end_y+1)
xor $80
ld (g_cmp),a
ld a,(g_y+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_58
jp nz,geo_done
ld a,(g_end_y)
ld (g_cmp),a
ld a,(g_y)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_58
jp nz,geo_done
geo_branch_58:
ld hl,(g_x)
ld (g_px),hl
ld hl,(g_y)
ld (g_py),hl
ld hl,(g_end_y)
ld (g_bottom),hl
call geo_vspan
jp geo_done
geo_curve_start:
; Collapsed axes are an inclusive point or straight span, never a division by zero.
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_rx+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_59
ld a,$00
ld (g_cmp),a
ld a,(g_rx)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_59
jp geo_curve_vertical
geo_branch_59:
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_ry+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_60
ld a,$00
ld (g_cmp),a
ld a,(g_ry)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_60
jp geo_curve_horizontal
geo_branch_60:
ld hl,(g_rx)
ld (g_x),hl
ld a,(g_x)
xor $ff
ld (g_x),a
ld a,(g_x+1)
xor $ff
ld (g_x+1),a
ld hl,(g_x)
ld de,$0001
add hl,de
ld (g_x),hl
ld hl,$0000
ld (g_y),hl
ld hl,$ffff
ld (g_last_y),hl
; Squared radii and signed error increments use 32-bit storage. Radii <=255.
ld a,$00
ld (g_a2),a
ld a,$00
ld (g_a2+1),a
ld a,$00
ld (g_a2+2),a
ld a,$00
ld (g_a2+3),a
ld a,$00
ld (g_work),a
ld a,$00
ld (g_work+1),a
ld a,$00
ld (g_work+2),a
ld a,$00
ld (g_work+3),a
ld a,(g_rx)
ld (g_work),a
ld a,(g_rx+1)
ld (g_work+1),a
ld a,(g_rx)
ld (g_mul),a
geo_branch_61:
ld a,$00
ld (g_cmp),a
ld a,(g_mul)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_63
jp geo_branch_62
geo_branch_63:
ld a,(g_mul)
and 1
jp z,geo_branch_64
or a
ld a,(g_work)
ld b,a
ld a,(g_a2)
adc a,b
ld (g_a2),a
ld a,(g_work+1)
ld b,a
ld a,(g_a2+1)
adc a,b
ld (g_a2+1),a
ld a,(g_work+2)
ld b,a
ld a,(g_a2+2)
adc a,b
ld (g_a2+2),a
ld a,(g_work+3)
ld b,a
ld a,(g_a2+3)
adc a,b
ld (g_a2+3),a
geo_branch_64:
ld hl,g_mul
srl (hl)
ld hl,g_work
sla (hl)
inc hl
rl (hl)
inc hl
rl (hl)
inc hl
rl (hl)
jp geo_branch_61
geo_branch_62:
ld a,$00
ld (g_b2),a
ld a,$00
ld (g_b2+1),a
ld a,$00
ld (g_b2+2),a
ld a,$00
ld (g_b2+3),a
ld a,$00
ld (g_work),a
ld a,$00
ld (g_work+1),a
ld a,$00
ld (g_work+2),a
ld a,$00
ld (g_work+3),a
ld a,(g_ry)
ld (g_work),a
ld a,(g_ry+1)
ld (g_work+1),a
ld a,(g_ry)
ld (g_mul),a
geo_branch_65:
ld a,$00
ld (g_cmp),a
ld a,(g_mul)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_67
jp geo_branch_66
geo_branch_67:
ld a,(g_mul)
and 1
jp z,geo_branch_68
or a
ld a,(g_work)
ld b,a
ld a,(g_b2)
adc a,b
ld (g_b2),a
ld a,(g_work+1)
ld b,a
ld a,(g_b2+1)
adc a,b
ld (g_b2+1),a
ld a,(g_work+2)
ld b,a
ld a,(g_b2+2)
adc a,b
ld (g_b2+2),a
ld a,(g_work+3)
ld b,a
ld a,(g_b2+3)
adc a,b
ld (g_b2+3),a
geo_branch_68:
ld hl,g_mul
srl (hl)
ld hl,g_work
sla (hl)
inc hl
rl (hl)
inc hl
rl (hl)
inc hl
rl (hl)
jp geo_branch_65
geo_branch_66:
ld a,(g_b2)
ld (g_step_x),a
ld a,(g_b2+1)
ld (g_step_x+1),a
ld a,(g_b2+2)
ld (g_step_x+2),a
ld a,(g_b2+3)
ld (g_step_x+3),a
ld hl,g_step_x
sla (hl)
inc hl
rl (hl)
inc hl
rl (hl)
inc hl
rl (hl)
ld a,(g_a2)
ld (g_step_y),a
ld a,(g_a2+1)
ld (g_step_y+1),a
ld a,(g_a2+2)
ld (g_step_y+2),a
ld a,(g_a2+3)
ld (g_step_y+3),a
ld hl,g_step_y
sla (hl)
inc hl
rl (hl)
inc hl
rl (hl)
inc hl
rl (hl)
ld a,(g_b2)
ld (g_curve_dx),a
ld a,(g_b2+1)
ld (g_curve_dx+1),a
ld a,(g_b2+2)
ld (g_curve_dx+2),a
ld a,(g_b2+3)
ld (g_curve_dx+3),a
ld hl,(g_rx)
ld (g_temp),hl
geo_branch_69:
or a
ld a,(g_step_x)
ld b,a
ld a,(g_curve_dx)
sbc a,b
ld (g_curve_dx),a
ld a,(g_step_x+1)
ld b,a
ld a,(g_curve_dx+1)
sbc a,b
ld (g_curve_dx+1),a
ld a,(g_step_x+2)
ld b,a
ld a,(g_curve_dx+2)
sbc a,b
ld (g_curve_dx+2),a
ld a,(g_step_x+3)
ld b,a
ld a,(g_curve_dx+3)
sbc a,b
ld (g_curve_dx+3),a
ld hl,(g_temp)
ld de,$ffff
add hl,de
ld (g_temp),hl
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_temp+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_69
ld a,$00
ld (g_cmp),a
ld a,(g_temp)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_69
geo_branch_70:
ld a,(g_a2)
ld (g_curve_dy),a
ld a,(g_a2+1)
ld (g_curve_dy+1),a
ld a,(g_a2+2)
ld (g_curve_dy+2),a
ld a,(g_a2+3)
ld (g_curve_dy+3),a
ld a,(g_curve_dx)
ld (g_curve_err),a
ld a,(g_curve_dx+1)
ld (g_curve_err+1),a
ld a,(g_curve_dx+2)
ld (g_curve_err+2),a
ld a,(g_curve_dx+3)
ld (g_curve_err+3),a
or a
ld a,(g_curve_dy)
ld b,a
ld a,(g_curve_err)
adc a,b
ld (g_curve_err),a
ld a,(g_curve_dy+1)
ld b,a
ld a,(g_curve_err+1)
adc a,b
ld (g_curve_err+1),a
ld a,(g_curve_dy+2)
ld b,a
ld a,(g_curve_err+2)
adc a,b
ld (g_curve_err+2),a
ld a,(g_curve_dy+3)
ld b,a
ld a,(g_curve_err+3)
adc a,b
ld (g_curve_err+3),a
geo_curve_loop:
call geo_emit_curve
ld a,(g_curve_err)
ld (g_curve_e2),a
ld a,(g_curve_err+1)
ld (g_curve_e2+1),a
ld a,(g_curve_err+2)
ld (g_curve_e2+2),a
ld a,(g_curve_err+3)
ld (g_curve_e2+3),a
ld hl,g_curve_e2
sla (hl)
inc hl
rl (hl)
inc hl
rl (hl)
inc hl
rl (hl)
ld a,(g_curve_dx+3)
xor $80
ld (g_cmp),a
ld a,(g_curve_e2+3)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_71
jp nz,geo_branch_72
ld a,(g_curve_dx+2)
ld (g_cmp),a
ld a,(g_curve_e2+2)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_71
jp nz,geo_branch_72
ld a,(g_curve_dx+1)
ld (g_cmp),a
ld a,(g_curve_e2+1)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_71
jp nz,geo_branch_72
ld a,(g_curve_dx)
ld (g_cmp),a
ld a,(g_curve_e2)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_71
geo_branch_72:
ld hl,(g_x)
ld de,$0001
add hl,de
ld (g_x),hl
or a
ld a,(g_step_x)
ld b,a
ld a,(g_curve_dx)
adc a,b
ld (g_curve_dx),a
ld a,(g_step_x+1)
ld b,a
ld a,(g_curve_dx+1)
adc a,b
ld (g_curve_dx+1),a
ld a,(g_step_x+2)
ld b,a
ld a,(g_curve_dx+2)
adc a,b
ld (g_curve_dx+2),a
ld a,(g_step_x+3)
ld b,a
ld a,(g_curve_dx+3)
adc a,b
ld (g_curve_dx+3),a
or a
ld a,(g_curve_dx)
ld b,a
ld a,(g_curve_err)
adc a,b
ld (g_curve_err),a
ld a,(g_curve_dx+1)
ld b,a
ld a,(g_curve_err+1)
adc a,b
ld (g_curve_err+1),a
ld a,(g_curve_dx+2)
ld b,a
ld a,(g_curve_err+2)
adc a,b
ld (g_curve_err+2),a
ld a,(g_curve_dx+3)
ld b,a
ld a,(g_curve_err+3)
adc a,b
ld (g_curve_err+3),a
geo_branch_71:
ld a,(g_curve_dy+3)
xor $80
ld (g_cmp),a
ld a,(g_curve_e2+3)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_74
jp nz,geo_branch_73
ld a,(g_curve_dy+2)
ld (g_cmp),a
ld a,(g_curve_e2+2)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_74
jp nz,geo_branch_73
ld a,(g_curve_dy+1)
ld (g_cmp),a
ld a,(g_curve_e2+1)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_74
jp nz,geo_branch_73
ld a,(g_curve_dy)
ld (g_cmp),a
ld a,(g_curve_e2)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_74
jp nz,geo_branch_73
geo_branch_74:
ld hl,(g_y)
ld de,$0001
add hl,de
ld (g_y),hl
or a
ld a,(g_step_y)
ld b,a
ld a,(g_curve_dy)
adc a,b
ld (g_curve_dy),a
ld a,(g_step_y+1)
ld b,a
ld a,(g_curve_dy+1)
adc a,b
ld (g_curve_dy+1),a
ld a,(g_step_y+2)
ld b,a
ld a,(g_curve_dy+2)
adc a,b
ld (g_curve_dy+2),a
ld a,(g_step_y+3)
ld b,a
ld a,(g_curve_dy+3)
adc a,b
ld (g_curve_dy+3),a
or a
ld a,(g_curve_dy)
ld b,a
ld a,(g_curve_err)
adc a,b
ld (g_curve_err),a
ld a,(g_curve_dy+1)
ld b,a
ld a,(g_curve_err+1)
adc a,b
ld (g_curve_err+1),a
ld a,(g_curve_dy+2)
ld b,a
ld a,(g_curve_err+2)
adc a,b
ld (g_curve_err+2),a
ld a,(g_curve_dy+3)
ld b,a
ld a,(g_curve_err+3)
adc a,b
ld (g_curve_err+3),a
geo_branch_73:
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_x+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_curve_loop
jp nz,geo_branch_75
ld a,$00
ld (g_cmp),a
ld a,(g_x)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_curve_loop
jp z,geo_curve_loop
geo_branch_75:
ld hl,$0000
ld (g_x),hl
geo_curve_tip:
ld a,(g_ry+1)
xor $80
ld (g_cmp),a
ld a,(g_y+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_76
jp nz,geo_done
ld a,(g_ry)
ld (g_cmp),a
ld a,(g_y)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_76
jp geo_done
geo_branch_76:
ld hl,(g_y)
ld de,$0001
add hl,de
ld (g_y),hl
call geo_emit_curve
jp geo_curve_tip
geo_curve_vertical:
ld hl,(g_a)
ld (g_px),hl
ld hl,(g_b)
ld (g_py),hl
ld hl,(g_py)
ld de,(g_ry)
or a
sbc hl,de
ld (g_py),hl
ld hl,(g_b)
ld (g_bottom),hl
ld hl,(g_bottom)
ld de,(g_ry)
add hl,de
ld (g_bottom),hl
call geo_vspan
jp geo_done
geo_curve_horizontal:
ld hl,(g_b)
ld (g_py),hl
ld hl,(g_a)
ld (g_px),hl
ld hl,(g_px)
ld de,(g_rx)
or a
sbc hl,de
ld (g_px),hl
ld hl,(g_a)
ld (g_right),hl
ld hl,(g_right)
ld de,(g_rx)
add hl,de
ld (g_right),hl
call geo_span
jp geo_done
geo_emit_curve:
ld a,$00
ld (g_cmp),a
ld a,(g_fill)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_emit_fill
geo_branch_77:
; Axis tests remove duplicate symmetric pixels, including XOR at the centre.
ld hl,(g_a)
ld (g_px),hl
ld hl,(g_px)
ld de,(g_x)
or a
sbc hl,de
ld (g_px),hl
ld hl,(g_b)
ld (g_py),hl
ld hl,(g_py)
ld de,(g_y)
add hl,de
ld (g_py),hl
call geo_point
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_x+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_79
ld a,$00
ld (g_cmp),a
ld a,(g_x)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_79
jp geo_branch_78
geo_branch_79:
ld hl,(g_a)
ld (g_px),hl
ld hl,(g_px)
ld de,(g_x)
add hl,de
ld (g_px),hl
ld hl,(g_b)
ld (g_py),hl
ld hl,(g_py)
ld de,(g_y)
add hl,de
ld (g_py),hl
call geo_point
geo_branch_78:
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_y+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_80
ld a,$00
ld (g_cmp),a
ld a,(g_y)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_80
jp geo_emit_return
geo_branch_80:
ld hl,(g_a)
ld (g_px),hl
ld hl,(g_px)
ld de,(g_x)
or a
sbc hl,de
ld (g_px),hl
ld hl,(g_b)
ld (g_py),hl
ld hl,(g_py)
ld de,(g_y)
or a
sbc hl,de
ld (g_py),hl
call geo_point
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_x+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_81
ld a,$00
ld (g_cmp),a
ld a,(g_x)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_81
jp geo_emit_return
geo_branch_81:
ld hl,(g_a)
ld (g_px),hl
ld hl,(g_px)
ld de,(g_x)
add hl,de
ld (g_px),hl
ld hl,(g_b)
ld (g_py),hl
ld hl,(g_py)
ld de,(g_y)
or a
sbc hl,de
ld (g_py),hl
call geo_point
geo_emit_return:
ret
geo_emit_fill:
; Only the first, widest extent for a row emits spans. Each row is written once.
ld a,(g_last_y+1)
xor $80
ld (g_cmp),a
ld a,(g_y+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_82
ld a,(g_last_y)
ld (g_cmp),a
ld a,(g_y)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_82
jp geo_emit_return
geo_branch_82:
ld hl,(g_y)
ld (g_last_y),hl
ld hl,(g_a)
ld (g_px),hl
ld hl,(g_px)
ld de,(g_x)
add hl,de
ld (g_px),hl
ld hl,(g_a)
ld (g_right),hl
ld hl,(g_right)
ld de,(g_x)
or a
sbc hl,de
ld (g_right),hl
ld hl,(g_b)
ld (g_py),hl
ld hl,(g_py)
ld de,(g_y)
add hl,de
ld (g_py),hl
call geo_span
ld a,$00
xor $80
ld (g_cmp),a
ld a,(g_y+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_83
ld a,$00
ld (g_cmp),a
ld a,(g_y)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp nz,geo_branch_83
jp geo_emit_return
geo_branch_83:
ld hl,(g_a)
ld (g_px),hl
ld hl,(g_px)
ld de,(g_x)
add hl,de
ld (g_px),hl
ld hl,(g_a)
ld (g_right),hl
ld hl,(g_right)
ld de,(g_x)
or a
sbc hl,de
ld (g_right),hl
ld hl,(g_b)
ld (g_py),hl
ld hl,(g_py)
ld de,(g_y)
or a
sbc hl,de
ld (g_py),hl
call geo_span
ret
geo_point:
; Scissor the original raster, preserving its decision phase outside the window.
ld a,(g_clip_l+1)
xor $80
ld (g_cmp),a
ld a,(g_px+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_emit_return
jp nz,geo_branch_84
ld a,(g_clip_l)
ld (g_cmp),a
ld a,(g_px)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_emit_return
geo_branch_84:
ld a,(g_clip_r+1)
xor $80
ld (g_cmp),a
ld a,(g_px+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_85
jp nz,geo_emit_return
ld a,(g_clip_r)
ld (g_cmp),a
ld a,(g_px)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_85
jp geo_emit_return
geo_branch_85:
ld a,(g_clip_t+1)
xor $80
ld (g_cmp),a
ld a,(g_py+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_emit_return
jp nz,geo_branch_86
ld a,(g_clip_t)
ld (g_cmp),a
ld a,(g_py)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_emit_return
geo_branch_86:
ld a,(g_clip_b+1)
xor $80
ld (g_cmp),a
ld a,(g_py+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_87
jp nz,geo_emit_return
ld a,(g_clip_b)
ld (g_cmp),a
ld a,(g_py)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_87
jp geo_emit_return
geo_branch_87:
ld hl,(g_px)
ld (bp_x0),hl
ld hl,(g_py)
ld (bp_y0),hl
call geo_backend_inputs
call bp_pixel
jp geo_account
geo_span:
ld a,(g_clip_t+1)
xor $80
ld (g_cmp),a
ld a,(g_py+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_emit_return
jp nz,geo_branch_88
ld a,(g_clip_t)
ld (g_cmp),a
ld a,(g_py)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_emit_return
geo_branch_88:
ld a,(g_clip_b+1)
xor $80
ld (g_cmp),a
ld a,(g_py+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_89
jp nz,geo_emit_return
ld a,(g_clip_b)
ld (g_cmp),a
ld a,(g_py)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_89
jp geo_emit_return
geo_branch_89:
ld a,(g_clip_l+1)
xor $80
ld (g_cmp),a
ld a,(g_right+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_emit_return
jp nz,geo_branch_90
ld a,(g_clip_l)
ld (g_cmp),a
ld a,(g_right)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_emit_return
geo_branch_90:
ld a,(g_clip_r+1)
xor $80
ld (g_cmp),a
ld a,(g_px+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_91
jp nz,geo_emit_return
ld a,(g_clip_r)
ld (g_cmp),a
ld a,(g_px)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_91
jp geo_emit_return
geo_branch_91:
ld a,(g_clip_l+1)
xor $80
ld (g_cmp),a
ld a,(g_px+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_93
jp nz,geo_branch_92
ld a,(g_clip_l)
ld (g_cmp),a
ld a,(g_px)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_93
jp geo_branch_92
geo_branch_93:
ld hl,(g_clip_l)
ld (g_px),hl
geo_branch_92:
ld a,(g_clip_r+1)
xor $80
ld (g_cmp),a
ld a,(g_right+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_94
jp nz,geo_branch_95
ld a,(g_clip_r)
ld (g_cmp),a
ld a,(g_right)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_94
geo_branch_95:
ld hl,(g_clip_r)
ld (g_right),hl
ld hl,(g_right)
ld de,$ffff
add hl,de
ld (g_right),hl
geo_branch_94:
ld hl,(g_px)
ld (bp_x0),hl
ld hl,(g_py)
ld (bp_y0),hl
ld hl,(g_right)
ld (bp_x1),hl
ld hl,(bp_x1)
ld de,$0001
add hl,de
ld (bp_x1),hl
call geo_backend_inputs
call bp_hspan
jp geo_account
geo_vspan:
ld a,(g_clip_l+1)
xor $80
ld (g_cmp),a
ld a,(g_px+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_emit_return
jp nz,geo_branch_96
ld a,(g_clip_l)
ld (g_cmp),a
ld a,(g_px)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_emit_return
geo_branch_96:
ld a,(g_clip_r+1)
xor $80
ld (g_cmp),a
ld a,(g_px+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_97
jp nz,geo_emit_return
ld a,(g_clip_r)
ld (g_cmp),a
ld a,(g_px)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_97
jp geo_emit_return
geo_branch_97:
ld a,(g_clip_t+1)
xor $80
ld (g_cmp),a
ld a,(g_bottom+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_emit_return
jp nz,geo_branch_98
ld a,(g_clip_t)
ld (g_cmp),a
ld a,(g_bottom)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_emit_return
geo_branch_98:
ld a,(g_clip_b+1)
xor $80
ld (g_cmp),a
ld a,(g_py+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_99
jp nz,geo_emit_return
ld a,(g_clip_b)
ld (g_cmp),a
ld a,(g_py)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_99
jp geo_emit_return
geo_branch_99:
ld a,(g_clip_t+1)
xor $80
ld (g_cmp),a
ld a,(g_py+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_101
jp nz,geo_branch_100
ld a,(g_clip_t)
ld (g_cmp),a
ld a,(g_py)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_101
jp geo_branch_100
geo_branch_101:
ld hl,(g_clip_t)
ld (g_py),hl
geo_branch_100:
ld a,(g_clip_b+1)
xor $80
ld (g_cmp),a
ld a,(g_bottom+1)
xor $80
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_102
jp nz,geo_branch_103
ld a,(g_clip_b)
ld (g_cmp),a
ld a,(g_bottom)
ld b,a
ld a,(g_cmp)
ld c,a
ld a,b
cp c
jp c,geo_branch_102
geo_branch_103:
ld hl,(g_clip_b)
ld (g_bottom),hl
ld hl,(g_bottom)
ld de,$ffff
add hl,de
ld (g_bottom),hl
geo_branch_102:
ld hl,(g_px)
ld (bp_x0),hl
ld hl,(g_py)
ld (bp_y0),hl
ld hl,(g_bottom)
ld (bp_y1),hl
ld hl,(bp_y1)
ld de,$0001
add hl,de
ld (bp_y1),hl
call geo_backend_inputs
call bp_vspan
jp geo_account
geo_backend_inputs:
ld a,(g_op)
ld (bp_op),a
ld a,(g_pen)
ld (bp_pen),a
ret
geo_account:
ld a,$00
ld (g_work),a
ld a,$00
ld (g_work+1),a
ld a,$00
ld (g_work+2),a
ld a,$00
ld (g_work+3),a
ld a,(bp_writes)
ld (g_work),a
ld a,(bp_writes+1)
ld (g_work+1),a
or a
ld a,(g_work)
ld b,a
ld a,(g_writes)
adc a,b
ld (g_writes),a
ld a,(g_work+1)
ld b,a
ld a,(g_writes+1)
adc a,b
ld (g_writes+1),a
ld a,(g_work+2)
ld b,a
ld a,(g_writes+2)
adc a,b
ld (g_writes+2),a
ld a,(g_work+3)
ld b,a
ld a,(g_writes+3)
adc a,b
ld (g_writes+3),a
ld hl,(g_calls)
ld de,$0001
add hl,de
ld (g_calls),hl
ret
geometry_end:What was verified
215 calls completed on this exact configuration using Asm198x 0.0.71 and Emu198x 0.39.0. Every call compares complete bitmap memory, neighbouring guards, public inputs, acceptance, byte writes and backend calls with an independent model. Cases cover all octants, reversal/ties, endpoint ownership, axes, zero radii, clipping, XOR, range extremes and rejected requests. Selected whole native frames match independently composed pixels. A wrong ellipse recurrence completed accepted draws and failed both memory and picture checks.
The radius bound also has a separate exhaustive arithmetic check: all 65,025 nonzero radius pairs fit the chosen 32-bit error path. That host check does not claim native execution of every pair. Real-hardware timing, stock-OS loading and tear-free live drawing remain separate.