Original Code198x program and artwork captured in Emu198x.
Code status: maintained routines and an emulator-executed phase gallery. Execution record.
The aligned renderer can draw a byte column. This wrapper lets the object keep a pixel X position and selects the prepared source that belongs there. The gallery exercises every phase in the interior and against each screen edge.
Supply pixel position and a bank
Include phase.inc, this folder’s draw.inc, the existing clipped-sprites/clip.inc and bitmap-addressing helper once. Include a generated phase bank once. draw_pixel_sprite takes IX pointing at 32 exclusive caller bytes: signed pixel X at +0, signed Y at +14, and an eight-entry phase pointer table at +28.
X and table fields are consumed: they become the aligned byte X and chosen source pointer. Reload all three inputs for each call, retaining logical game coordinates elsewhere. The complete caller below shows those assignments before every draw. This also lets several objects use the routine with different generated banks.
AF/BC/DE/HL change; IX/IY survive. Keep table, source, context, code and stack separate from the bitmap and one another. The include changes neither attributes nor interrupt setup. Exclude concurrent users of the working state.
Split signed pixel X and select a prepared source
; SPDX-License-Identifier: MIT
; draw_pixel_sprite: IX -> 32 bytes, +0 signed PIXEL X, +14 signed Y,
; +28 pointer to an 8-entry phase pointer table (generated KEEP/image rows).
; Consumes X/table fields: they become byte X/source pointer. Reload each call.
; AF/BC/DE/HL change, IX/IY survive. Ownership: same as phase_draw_clipped.
; Include qualified clip_axis, bitmap addressing and this folder's draw.inc once.
draw_pixel_sprite:
ld l,(ix+0)
ld h,(ix+1)
ld a,l
and 7
; Arithmetic division rounds toward -infinity, including negative X.
sra h
rr l
sra h
rr l
sra h
rr l
ld (ix+0),l
ld (ix+1),h
ld l,(ix+28)
ld h,(ix+29)
ld e,a
ld d,0
add hl,de
add hl,de
ld e,(hl)
inc hl
ld d,(hl)
ld (ix+28),e
ld (ix+29),d
jp phase_draw_clippedThe low three bits choose a phase. Arithmetic shifts turn −1 into byte column −1, rather than zero or a large unsigned address. The shared article shows why that distinction matters.
Draw the selected padded image
The prepared source spans 24×16 pixels: three mask/image pairs per row, ninety-six bytes per phase. Transparent padding makes that fixed shape useful at every phase, including zero. The bounded copy loop clips the padded rectangle, advances through the correct source samples and keeps the original six-byte row stride.
Copy clipped prepared rows without losing source stride
; SPDX-License-Identifier: MIT
; phase_draw_clipped: IX -> 32 bytes. +0 signed X in 8-pixel byte columns,
; +14 signed Y in pixel rows, +28 pointer to 24x16 interleaved KEEP-mask/image.
; Other fields are work/output: two clip_axis blocks, +30 row counter.
; 96 source bytes remain caller-owned, separate from bitmap/code/stack/context.
; AF/BC/DE/HL change, IX/IY survive. Attributes untouched. Include clip.inc and
; bitmap-addressing.inc once. Exclusive workspace; no timing/IRQ setup here.
phase_draw_clipped:
ld (ix+2),3
ld (ix+3),0
ld (ix+4),32
ld (ix+5),0
call clip_axis
ld a,(ix+10)
or a
ret z
push ix
pop hl
ld de,14
add hl,de
push ix
push hl
pop ix
ld (ix+2),16
ld (ix+3),0
ld (ix+4),192
ld (ix+5),0
call clip_axis
pop ix
ld a,(ix+24)
or a
ret z
ld (ix+30),a
ld l,(ix+22)
ld h,0
add hl,hl
ld d,h
ld e,l
add hl,hl
add hl,de
ld e,(ix+8)
ld d,0
add hl,de
add hl,de
ld e,(ix+28)
ld d,(ix+29)
add hl,de
push hl
ld d,(ix+20)
ld a,(ix+6)
add a,a
add a,a
add a,a
ld e,a
call pixel_addr
ex de,hl
pop hl
phase_draw_clipped_row:
push hl
push de
ld b,(ix+10)
phase_draw_clipped_byte:
ld c,(hl)
inc hl
ld a,(de)
and c
or (hl)
ld (de),a
inc hl
inc de
djnz phase_draw_clipped_byte
pop de
pop hl
dec (ix+30)
ret z
ld bc,6
add hl,bc
ex de,hl
call pixel_down
ex de,hl
jr phase_draw_clipped_rowA bank uses 784 bytes including pointers, versus 64 unshifted bytes. The drawing loop pushes four row-pointer bytes, plus call-return and interrupt requirements. The clipping helper keeps its existing contract; this is a separate renderer for the wider prepared rows.
Build and adapt the caller
Build with make ASM198X=/path/to/asm198x; load build/demo.tap on a 48K PAL Spectrum. The caller owns the bitmap, attributes and a private stack at $ff00, then uses ROM IM1/HALT to service frames. Reset to leave. It draws a static gallery, so it does not promise a smooth animation cadence.
Complete tape caller for every phase and all four edges
org $8000
start:
di
ld sp,$ff00
ld hl,$4000
ld de,$4001
ld bc,6143
ld (hl),0
ldir
ld hl,$5800
ld de,$5801
ld bc,767
ld (hl),71
ldir
ld d,0
paint_line:
ld e,0
call pixel_addr
ld b,32
paint_byte:
ld (hl),$11
inc hl
djnz paint_byte
ld a,d
add a,8
ld d,a
cp 192
jr c,paint_line
ld hl,8
ld (context),hl
ld hl,40
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,33
ld (context),hl
ld hl,40
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,58
ld (context),hl
ld hl,40
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,83
ld (context),hl
ld hl,40
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,108
ld (context),hl
ld hl,40
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,133
ld (context),hl
ld hl,40
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,158
ld (context),hl
ld hl,40
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,183
ld (context),hl
ld hl,40
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,8
ld (context),hl
ld hl,-8
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,33
ld (context),hl
ld hl,-8
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,58
ld (context),hl
ld hl,-8
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,83
ld (context),hl
ld hl,-8
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,108
ld (context),hl
ld hl,-8
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,133
ld (context),hl
ld hl,-8
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,158
ld (context),hl
ld hl,-8
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,183
ld (context),hl
ld hl,-8
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,8
ld (context),hl
ld hl,184
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,33
ld (context),hl
ld hl,184
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,58
ld (context),hl
ld hl,184
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,83
ld (context),hl
ld hl,184
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,108
ld (context),hl
ld hl,184
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,133
ld (context),hl
ld hl,184
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,158
ld (context),hl
ld hl,184
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,183
ld (context),hl
ld hl,184
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,-8
ld (context),hl
ld hl,80
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,-7
ld (context),hl
ld hl,90
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,-6
ld (context),hl
ld hl,100
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,-5
ld (context),hl
ld hl,110
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,-4
ld (context),hl
ld hl,120
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,-3
ld (context),hl
ld hl,130
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,-2
ld (context),hl
ld hl,140
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,-1
ld (context),hl
ld hl,150
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,248
ld (context),hl
ld hl,80
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,249
ld (context),hl
ld hl,90
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,250
ld (context),hl
ld hl,100
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,251
ld (context),hl
ld hl,110
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,252
ld (context),hl
ld hl,120
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,253
ld (context),hl
ld hl,130
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,254
ld (context),hl
ld hl,140
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,255
ld (context),hl
ld hl,150
ld (context+14),hl
ld hl,sprite_phase_table
ld (context+28),hl
ld ix,context
call draw_pixel_sprite
ld hl,$cafe
ld (ready),hl
im 1
ei
main:
halt
ld hl,(updates)
inc hl
ld (updates),hl
jr main
ready: defw 0
updates: defw 0
context: defs 32,0
include "phase.inc"
include "draw.inc"
include "../clipped-sprites/clip.inc"
include "../bitmap-addressing/bitmap-addressing.inc"
include "phases.inc"
end startChange the original sprite.json, then run make regenerate; make check-assets verifies the committed bank. For a moving object, the existing rectangle save/restore helpers accept the three-byte width. Allow up to 48 saved background bytes per object and keep its old clipped address and dimensions. Source preparation, position updates and removal of the old picture remain separate jobs.