Breakout

TUTORIAL · BREAKOUT · UNDER THE HOOD ABETA

A · Sprites from a companion ROM

The game's graphics live in their own 16 KB sideways ROM, and EGFX reads them straight from there: nothing is loaded from disc, no sideways RAM is needed, and no BASIC memory is used.

What is in EGASSETS.rom

ROM addressPackFrames
&8100EGSP v3, 10 frames of 16×160 legacy bat · 1 legacy ball · 2–7 one bevelled brick per row · 8 full opaque eraser · 9 half-height brick eraser
&8700EGSP v3, 2 frames of 8×80 ball · 1 opaque black eraser

The ROM is a valid MOS service ROM that ignores every service call: it exists only to hold the packs. An EGSP v3 pack is a 9-byte header (EGSP, version 3, frame count, width, height, bits per pixel) followed by all the pixel frames, then all the mask frames. A mask of 00 writes the pixel's colour (black included) and 11 keeps the background, so an "eraser" is just a fully opaque black frame, and erasing costs exactly the same as drawing.

Two ways to draw from it

20 PROCeginit                                    :REM start EGFX: checks TOP<=&2200 and HIMEM=&2400,
    REM fills the 64-byte request block and makes sure EGFX is fitted
    :PROCegcompanion(7,&81,255)                  :REM register the 16x16 sprite pack at &8100 in
    REM slot 7 for the brick batches (255: no sideways RAM history bank)
    :DIM E% 15                                   :REM 16 bytes for EAudio's OSWORD &C0 block
    REM (E% is a resident integer: one letter, cheap to tokenise)
    :!E%=&1040                                   :REM bytes 0 and 1 in one store: 64 out, 16 back
    :$(E%+2)="EAUD"                              :REM bytes 2-5: the signature EAudio answers to
    :E%?6=0:PROCe                                :REM command 0: INIT (overwrites the string's CR)
    :E%?6=7:E%?8=3:PROCe                         :REM command 7: VOLUME 3. A freshly switched-on
    REM machine holds 0 here, which is mute, and EAudio still says yes
1820 DEF PROCegcompanion(b%,p%,h%):egb%?8=b%:egb%?9=p%:egb%?10=h%:PROCegcall(33):ENDPROC
2110 DEF PROCegcompact(x%,y%,i%,f%,b%,p%)
2120 PROCegxy(x%,y%):egb%?12=i%:egb%?14=f%:egb%?16=b%:egb%?17=p%:PROCegcall(37):ENDPROC

PROCegcompanion(7,&81,255) (function 33) registers the 16×16 pack in slot 7 as the current sprite resource (255 means no writable history bank), and registered frames can then be drawn by the batch call, function 34, which is how the brick wall goes up. PROCegcompact (function 37) needs no registration: each call names the slot and page of any EGSP v3 pack, and the ROM checks the header and frame before drawing. The game uses it for the ball (&87) and for brick repairs (&81).

Choosing sprite sizes

Frame sizePixel + mask bytes per frame
4×48
8×832
16×16128
32×32512

The compact renderer walks every row of the frame it is given. Drawing a small ball inside a mostly transparent 16×16 frame costs about as much as a full 16×16 sprite, which is why the ball has its own 8×8 pack. Make the frame the size of the art.

Editing the art: open the packs in Beeb-dev's Sprite Editor. In the source repository the packs are tools/bbc/breakout/SPRITES and BALL8, and make-asset-rom.py places them at &8100 and &8700.

← Stage 8: Sound Repainting only what moved →