Skip to content

Place Amiga Bobs with Prepared Pixel Phases

Trade chip RAM for sixteen prepared mask/image phases, reusing the checked word-aligned clipped blitter renderer at individual pixel positions.

Original amiga sprite gallery with prepared pixel phases on a sparse background grid, including cropped screen edges

Original Code198x program and artwork captured in Emu198x.

Code status: maintained routines and an emulator-executed phase gallery. Execution record.

The Amiga’s blitter can shift images itself. This implementation explores another useful trade-off: store prepared phases and reuse the existing bounded word-aligned renderer. It spends chip RAM to keep phase selection small and the clipped drawing path familiar.

Pass pixel X and the phase table

Include phase.inc, the existing clipped-sprites/clip.inc, clipped-sprites/draw.inc and a generated bank in chip RAM. draw_pixel_sprite takes signed pixel X in D0.w, Y in D1.w, screen base in A2 and a sixteen-entry table in A3. Each table entry contains an opaque-mask pointer followed by an image pointer.

D0-D7 and A0-A5 survive; CCR changes. The wrapper saves 36 stack bytes in addition to the nested renderer’s 68, plus call-return and interrupt requirements. All pointers must be even; source, table, screen, code and stack must be separate owned chip-RAM spans. Enable blitter DMA and exclude other blitter users for the entire synchronous call. The include sets up no copper list, OS ownership or interrupts.

Select prepared mask/image pointers from signed pixel X

phase.inc
; SPDX-License-Identifier: MIT
; draw_pixel_sprite: D0.w signed PIXEL X, D1.w Y, A2 screen, A3 phase table.
; Table has 16 pairs of opaque-mask/image chip-RAM pointers for a 16x16 source,
; padded to 32x16 at each phase. Reuses the qualified clipped bob renderer.
; D0-D7/A0-A5 survive, CCR changes. Own/enable blitter; even owned chip spans.
; Saves 36 bytes in addition to the nested renderer's 68, plus returns/IRQs.
draw_pixel_sprite:
    movem.l d0-d3/a0-a4,-(sp)
    move.w d0,d3
    and.w #15,d3
    lsl.w #3,d3
    asr.w #4,d0
    move.l (a3,d3.w),a0
    lea 4(a3),a4
    move.l (a4,d3.w),a1
    bsr draw_clipped
    movem.l (sp)+,d0-d3/a0-a4
    rts

The low four X bits select the phase; arithmetic division finds the word column. At X = −1, word −1 and phase 15 place the surviving pixels correctly at the left edge. The existing clipped renderer then handles both axes and preserves original source and screen strides.

Account for the chip RAM

Original artwork is 16×16. Each phase is padded to 32×16, using sixty-four bytes of opaque mask and sixty-four of image. Mask 1 selects image; mask 0 preserves the bitplane. That polarity differs from the Spectrum/BBC keep masks.

Sixteen prepared phases use 2,048 bytes. Add 128 bytes of pointer pairs and the whole bank is 2,176 bytes, compared with 64 unshifted mask/image bytes. Pointers are relocated by the normal assembler/loader; the preparation tool does not guess runtime addresses.

Use make regenerate after changing the original artwork and make check-assets to verify the bank. A distinct generation prefix gives another sprite its own labels and table for the same runtime routine.

Boot the complete caller

Build with make ASM198X=/path/to/asm198x BUILD198X=/path/to/build198x-adf, or use the publishing toolchain’s default build198x adf interface. Boot build/demo.adf on A500 OCS PAL/Kickstart1.3. The caller owns custom chips and one 320×256 bitplane, draws all sixteen phases against interior and edge positions, then continues frame servicing. Reset to leave.

Original ADF phase gallery using the existing clipped blitter path

demo.asm
; A500 OCS PAL/KS1.3, all data in chip RAM. Owns chips; reset to leave.
    section code,code_c
start:
    lea $dff000,a5
    move.w #$7fff,$09a(a5)
    move.w #$7fff,$09c(a5)
    move.w #$7fff,$096(a5)
    lea plane,a0
    move.w #255,d7
paint_row:
    move.w #19,d6
paint_word:
    move.w d7,d0
    not.w d0
    and.w #7,d0
    moveq #0,d1
    tst.w d0
    bne paint_value
    move.w #$1111,d1
paint_value:
    move.w d1,(a0)+
    dbra d6,paint_word
    dbra d7,paint_row
    lea plane,a0
    move.l a0,d0
    lea copbpl,a1
    move.w d0,6(a1)
    swap d0
    move.w d0,2(a1)
    lea copperlist,a0
    move.l a0,$080(a5)
    move.w #0,$088(a5)
    move.w #$83c0,$096(a5)
    move.w #16,d0
    move.w #40,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #49,d0
    move.w #40,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #82,d0
    move.w #40,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #115,d0
    move.w #40,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #148,d0
    move.w #40,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #181,d0
    move.w #40,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #214,d0
    move.w #40,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #247,d0
    move.w #40,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #24,d0
    move.w #72,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #57,d0
    move.w #72,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #90,d0
    move.w #72,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #123,d0
    move.w #72,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #156,d0
    move.w #72,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #189,d0
    move.w #72,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #222,d0
    move.w #72,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #255,d0
    move.w #72,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #16,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #49,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #82,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #115,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #148,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #181,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #214,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #247,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #24,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #57,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #90,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #123,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #156,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #189,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #222,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #255,d0
    move.w #-8,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #16,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #49,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #82,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #115,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #148,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #181,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #214,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #247,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #24,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #57,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #90,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #123,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #156,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #189,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #222,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #255,d0
    move.w #248,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-16,d0
    move.w #80,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-15,d0
    move.w #90,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-14,d0
    move.w #100,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-13,d0
    move.w #110,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-12,d0
    move.w #120,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-11,d0
    move.w #130,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-10,d0
    move.w #140,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-9,d0
    move.w #150,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-8,d0
    move.w #160,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-7,d0
    move.w #170,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-6,d0
    move.w #180,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-5,d0
    move.w #190,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-4,d0
    move.w #200,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-3,d0
    move.w #210,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-2,d0
    move.w #220,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #-1,d0
    move.w #230,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #304,d0
    move.w #80,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #305,d0
    move.w #90,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #306,d0
    move.w #100,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #307,d0
    move.w #110,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #308,d0
    move.w #120,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #309,d0
    move.w #130,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #310,d0
    move.w #140,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #311,d0
    move.w #150,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #312,d0
    move.w #160,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #313,d0
    move.w #170,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #314,d0
    move.w #180,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #315,d0
    move.w #190,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #316,d0
    move.w #200,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #317,d0
    move.w #210,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #318,d0
    move.w #220,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #319,d0
    move.w #230,d1
    lea plane,a2
    lea sprite_phase_table,a3
    bsr draw_pixel_sprite
    move.w #$cafe,ready
main:
    bsr wait_frame
    addq.w #1,updates
    bra main
signature: dc.b "198XPHASES!",0
    even
ready: dc.w 0
updates: dc.w 0
    include "phase.inc"
    include "../clipped-sprites/clip.inc"
    include "../clipped-sprites/draw.inc"
    include "../../framework/game-loop/frame.inc"
    include "phases.inc"
    even
copperlist:
    dc.w $08e,$2c81,$090,$2cc1,$092,$0038,$094,$00d0
    dc.w $100,$1200,$102,0,$104,0,$108,0,$10a,0
    dc.w $180,$012,$182,$6ed
copbpl:
    dc.w $0e0,0,$0e2,0
    dc.w $ffff,$fffe
before_plane: dcb.b 16,$a7
plane: dcb.b 10240,0
after_plane: dcb.b 16,$a7

This is a static placement gallery, not a measured animation cadence or whole-game frame budget. A moving object still needs its previous padded/clipped footprint removed. The existing save/draw/restore routines can cover up to sixty-four saved background bytes here; retain the old address and dimensions with each buffer.

Compare the next branch

The hardware A/B shifters are the natural onward implementation when stored phase data is expensive. Their source carry, edge masks and source width need careful treatment; the Amiga Hardware Reference Manual, chapter 6 describes those mechanisms. The shared phase article compares preparation, runtime work and memory without hiding the machine’s alternatives.