Banshee is a 2D scrolling shooter, so the expected answer to the platform checklist's question — where does the frame end up? — is the boring one: planar assets blitted into a planar display, no chunky stage anywhere. That is what the disc does. It is worth recording as the fifth distinct answer the series has collected, and it is worth recording how it was established, because the three Blitter questions settle it in about ten minutes.
What is not boring is the display it does it into.
bans.exe's chip DATA hunk (hunk 4, file offset 0x20678) holds two copper-list
templates. Both are dumped in
notes/copper-lists.txt.
DIWSTRT $2c81 V 44, H 129
DIWSTOP $2cc1 V 300, H 449 -> 320 lores pixels x 256 lines
DDFSTRT $0038
DDFSTOP $00d0
BPLCON0 $0211 BPU = 8, lores, COLOR on, ECSENA set
BPLCON1 $0000
BPLCON2 $0224 PF1P = 4, PF2P = 4, KILLEHB set
BPLCON3 $0080 SPRES = 70 ns
BPLCON4 $0000 BPLAM = 0
BPL1MOD $0070 112
BPL2MOD $0070 112
FMODE $0007 64-bit bitplane fetch, 32-bit sprite fetch
BPLCON0 bit 4 is BPU3, so $0211 is eight bitplanes, not zero.
ECSENA is set and KILLEHB is set in BPLCON2, so there is no ambiguity of
the kind the platform checklist warns about: this is not six planes with
Extra-Half-Brite quietly on, it is a genuine AGA 256-colour display.
The palette is written as sixteen blocks of BPLCON3 + 32 COLORxx
moves:
0106 0000 bank 0, high nibbles 0180..01be 32 colours
0106 2000 bank 1 ...
0106 4000 bank 2
...
0106 e000 bank 7
0106 0200 bank 0, LOCT = 1 low nibbles
0106 2200 bank 1, LOCT
...
0106 e200 bank 7, LOCT
8 banks × 32 colours × 2 passes = 256 colours at 8 bits per channel. All of the picture files carry their palettes in exactly that form (below).
The second template, at file offset 0x21966, differs in five registers:
BPLCON0 $8211 HIRES set, BPU = 8, ECSENA
BPL1MOD $0228 552
BPL2MOD $0228 552
with the same DIWSTRT/DIWSTOP (320 colour clocks = 640 hires pixels),
the same DDFSTRT/DDFSTOP, the same FMODE $0007, and the same sixteen
palette banks. Eight bitplanes in hires needs the 64-bit fetch that FMODE 3
provides, and this is the only disc in this series that asks for it.
introscreen, banspic1 and banspic2 all unpack to exactly 164,864
bytes and have the same layout:
0x00000 1,024 bytes 256 colours, 2 words each: 0RGB high nibbles, 0rgb low
0x00400 163,840 bytes 640 x 256, 8 bitplanes, interleaved, 80 bytes per plane row
1,024 = 256 × 4 is the AGA BANK/LOCT pair format the copper list wants, so
the file is a straight copy of what the copper writes.
Render one with
python3 tools/planar.py _work/unp/introscreen out.png \
--w 640 --h 256 --planes 8 --offset 0x400 --interleaved \
--pal-rgb24 <palette>:0:256
introscreen is the game's title logo, banspic1 and banspic2 are
rendered aircraft. banspic2 uses only 119,760 of its 164,864 bytes
(72.6 %) — a smaller picture in the same buffer.
The screen-clear blit confirms the layout independently:
BLTCON0/1 $01000000 minterm $00, USED only, no A/B/C -- clear
BLTDMOD $0230 560
BLTSIZE $4028 height 256, width 40 words = 80 bytes
repeated with the destination stepping +80 bytes each time. 80 fetched + 560 modulo = 640 bytes per display row, and 640 × 256 = 163,840, the exact size of the bitmap half of a picture file. Eight of those blits, at 80-byte intervals, clear all eight interleaved planes.
The platform checklist's ten-minute test for "fills planar directly" versus "rasterises chunky and converts":
1. Is there a BLTCON1 write anywhere? Yes — exactly one that is reached
from a $dff000 base load, at file offset 0xA09A, and what it writes is:
0000a082 3c 00 move.w d0,d6
0000a084 02 46 00 0f andi.w #$f,d6 ; the low 4 bits of the x position
0000a088 e8 5e ror.w #$4,d6 ; into bits 15..12
...
0000a09a 33 46 00 42 move.w d6,$42(a1) ; BLTCON1 = BSH only
0000a09e 8c 6f 00 0a or.w $a(a7),d6 ; | minterm passed by the caller
0000a0a2 33 46 00 40 move.w d6,$40(a1) ; BLTCON0
BLTCON1 carries the B-shift and nothing else — no FILL bits, no
DESC, no FCI. So there is no area fill on this disc, and the shift is a
pixel-accurate horizontal position for a sprite, which is what a shooter
needs.
2. What is BLTSIZE's height, and what are the modulos? Screen-sized
rectangles, not scanlines:
h=256 w=40 words BLTDMOD 560 the full-screen clear
h=256 w=17 words BLTAMOD 302 BLTDMOD 62
h=192 w=17 words BLTDMOD 14
h=162 w=15 words BLTAMOD 26 BLTDMOD 66
h= 96 w=17 words BLTDMOD 62
h= 81 w=40 words
h=<object height> w=2 words the object blit, once per plane
Nothing has a height equal to the plane count and nothing has modulos of the
form bytes_per_row − width applied per scanline. This is not Guardian's
one-blit-per-scanline polygon filler; it is whole-rectangle copies and whole
screen clears.
3. Is the minterm $CA with USEA clear? $CA is there — four
sites write BLTCON0 = $0FCA — but with USEA set, not clear:
$0FCA minterm $CA USEA=1 USEB=1 USEC=1 USED=1
$0BFA minterm $FA USEA=1 USEB=0 USEC=1 USED=1
$0B0A minterm $0A USEA=1 USEB=0 USEC=1 USED=1
$09F0 minterm $F0 USEA=1 USEB=0 USEC=0 USED=1
$0100 minterm $00 USED only
$0FFE minterm $FE all four channels
$CA with all four channels enabled is D = AB + ¬AC — the ordinary masked
bob blit, A the mask, B the sprite, C the background, D the destination.
Guardian's cookie-cut is the same minterm with USEA clear and BLTADAT
loaded once outside the loop, which is a different operation with the same
number in it. The minterm alone does not identify the operation; read
USEA.
Together the three answers are: no fill, rectangles not scanlines, ordinary masked bobs. Banshee fills planar because its assets are planar and its display is planar, and there is no point in the pipeline at which a frame exists as chunky pixels.
The one routine that draws a moving object, at file offset 0xA062:
0000a062 lea $dff000,a1
0000a06c move.w d7,d2 ; d7 = object height
0000a06e lsl.w #$6,d2
0000a070 addq.w #$2,d2 ; BLTSIZE = (height << 6) | 2 -- 2 words wide
...
0000a07e lea $2(a3),a2 ; B = mask+2 (mask and data interleaved)
0000a09a move.w d6,$42(a1) ; BLTCON1 = shift
0000a0a2 move.w d6,$40(a1) ; BLTCON0 = shift | minterm
0000a0a6 movem.l a0/a2-a4,$48(a1); BLTCPT, BLTBPT, BLTAPT, BLTDPT in one instruction
0000a0ac move.w d2,$58(a1) ; BLTSIZE -- go
0000a0b4 move.w #$4,d1 ; and then five more times:
0000a0b8 addq.w #$2,a2
0000a0ba lea $6000(a4),a4 ; next plane, 0x6000 = 24,576 bytes on
...
0000a0de dbra d1,...
movem.l a0/a2-a4,$48(a1) writes BLTCPTH, BLTBPTH, BLTAPTH and
BLTDPTH as four consecutive longwords in one instruction — the standard
Amiga trick, and it is why a scan that counts individual move.l writes to
the Blitter pointers under-reports.
Six passes (d1 = 4 plus the one before the loop) at 0x6000 bytes apart
means six bitplanes per object into a non-interleaved buffer whose plane
stride is 24,576 bytes. The bossc/levelNc files are the chip-RAM halves
that feed it.
- No Akiko. Zero
lea $B80000,aN, zeromovea.l #$B80000,aN, zero$00B80038, zero$00B8003C, zero$C0DE0000, over both code hunks and both data hunks ofbans.exeand overpicture.exe. The five raw00 B8 00 38byte hits in the whole volume are all inside unpacked level data, where no pointer load could reach them. - No sprites in the game screen. The template points all eight sprite pointers at a zeroed longword.
- No dual playfield.
BPLCON0bit 10 is clear in both templates. - No
BPLCON4colour tricks.BPLAMis zero in both, so unlike Guardian there is no palette-bank offsetting.