BOBs: Blitter Objects
Software sprites on the Amiga
BOBs used the Amiga's Blitter to draw sprites into the playfield—offering unlimited objects at the cost of CPU coordination and flicker-free rendering.
The Amiga has only 8 hardware sprites, each 16 pixels wide. For games needing more objects—or larger, more colourful characters—programmers used BOBs (Blitter OBjects). The Blitter copied sprite graphics into the playfield bitplanes, with masking to preserve the background. More flexible than hardware sprites, but requiring careful buffer management.
The name is Commodore’s. The 1985 ROM Kernel Manual calls the playfield objects its animation system moves with the blitter “Bobs, for ‘blitter objects’”, and describes the save–draw–restore cycle below as what the system does for you. Game programmers borrowed the word for their own routines: Dave Jones, writing up Menace for Amiga Format in 1990, explains that “many people refer to software sprites on the Amiga as BOBS, short for Blitter OBjectS, as they tend to be drawn using the blitter”, and that in Menace “the main ship is made up of hardware sprites, but all of the aliens are software sprites”.
BOBs vs hardware sprites
| Feature | Hardware sprites | BOBs |
|---|---|---|
| Count | 8 (or 4 attached) | Unlimited* |
| Width | 16 pixels fixed | Any size |
| Colours | 3 or 15 (attached) | Playfield depth |
| Speed | Zero CPU | Blitter time |
| Background | Automatic | Requires save/restore |
*Limited by Blitter time and frame budget.
The BOB rendering process
1. Save background
Before drawing, save what’s underneath:
save_background:
; Copy screen area to save buffer
move.l bob_x,d0
move.l bob_y,d1
jsr calc_screen_address
; Blit from screen to save buffer
move.w #$09f0,BLTCON0(a6) ; A->D copy
move.l screen_addr,BLTAPT(a6)
move.l save_buffer,BLTDPT(a6)
move.w #(height<<6)|words,BLTSIZE(a6)
2. Draw BOB
Cookie-cut the sprite onto the screen:
draw_bob:
; A = mask data
; B = sprite data
; C = screen (background)
; D = screen (destination)
move.w #$0fca,BLTCON0(a6) ; cookie cut minterm
move.l mask_ptr,BLTAPT(a6)
move.l image_ptr,BLTBPT(a6)
move.l screen_addr,BLTCPT(a6)
move.l screen_addr,BLTDPT(a6)
; Set modulos for screen width
move.w screen_mod,BLTCMOD(a6)
move.w screen_mod,BLTDMOD(a6)
move.w #0,BLTAMOD(a6)
move.w #0,BLTBMOD(a6)
; Start blit
move.w #(height<<6)|words,BLTSIZE(a6)
3. Restore background (next frame)
Before drawing at new position, restore old background:
restore_background:
; Copy save buffer back to screen
move.l last_screen_addr,BLTDPT(a6)
move.l save_buffer,BLTAPT(a6)
move.w #$09f0,BLTCON0(a6)
move.w #(height<<6)|words,BLTSIZE(a6)
Double buffering with BOBs
Essential for flicker-free BOBs:
; Frame N:
; - Display buffer A
; - Clear BOBs from buffer B (restore backgrounds)
; - Draw BOBs to buffer B
; - Swap: B becomes display, A becomes draw
frame_update:
; Wait for VBlank
jsr wait_vblank
; Swap display pointers
move.l draw_buffer,d0
move.l display_buffer,draw_buffer
move.l d0,display_buffer
; Update copper to show new display buffer
jsr update_copper
; Now safe to modify draw_buffer
jsr restore_all_bobs
jsr update_bob_positions
jsr draw_all_bobs
Pixel-accurate positioning
The Blitter works on word boundaries. For sub-word positioning:
draw_bob_shifted:
; Calculate shift amount (0-15)
move.w bob_x,d0
and.w #$000f,d0 ; pixel offset within word
; Put shift value in BLTCON0/1
lsl.w #4,d0 ; shift to position
lsl.w #8,d0
or.w #$0fca,d0 ; add minterm
move.w d0,BLTCON0(a6)
When shifting, you need an extra word:
- 32-pixel BOB shifted = 3 words instead of 2
Sorting for draw order
BOBs must be drawn back-to-front (painter’s algorithm):
sort_bobs_by_y:
; Simple bubble sort (fine for small counts)
; Or use insertion sort as positions change incrementally
...
draw_sorted_bobs:
lea sorted_list,a0
.loop:
move.w (a0)+,d0 ; bob index
bmi .done ; -1 = end of list
jsr draw_single_bob
bra .loop
.done:
rts
Interleaved blitting
Overlap Blitter operations with CPU work:
; Don't wait after every blit
; Queue multiple operations
draw_multiple:
jsr start_bob1_blit ; start blitter
jsr do_game_logic ; CPU works while blitter runs
jsr wait_blitter ; ensure done before next
jsr start_bob2_blit
; ...
Memory requirements
| Component | Size |
|---|---|
| BOB image | width × height × planes |
| BOB mask | width × height |
| Save buffer | Same as BOB (per BOB) |
| Second screen | Full screen size |
Example: 32×32 BOB, 5 planes
- Image: 32×32×5 = 640 bytes
- Mask: 32×32 = 128 bytes
- Save: 32×32×5 = 640 bytes per instance
Performance considerations
| Factor | Impact |
|---|---|
| BOB size | Larger = slower |
| BOB count | More = slower |
| Bitplane depth | More planes = slower |
| Shifting | Adds a word to every row: a 2-word BOB blits as 3 |
| Screen width | Affects modulo |
Blitter cycle estimates
The Hardware Reference Manual gives the cost per word: a blitter cycle is four clock ticks minimum, plus two if channel B is enabled and two more if both C and D are. A cookie-cut (A, B, C and D) is therefore eight ticks per word, and an A→D copy four; the manual’s formula is t = n × H × W / 7.09 microseconds on PAL, with n the ticks per cycle and H and W the height and width in words, ignoring setup and DMA contention. For a 32×32 pixel BOB on a 5-bitplane playfield:
- 32 pixels = 2 words horizontally (3 if shifted to an arbitrary pixel position)
- 32 lines tall × 5 bitplanes = 160 line-passes
- Draw: 2 words × 160 passes × 8 ticks = 2,560 ticks, about 360 microseconds — 1.8% of a 20ms frame
- Save and restore: two A→D copies of 2 × 160 × 4 = 1,280 ticks each
The full save–draw–restore cycle for that BOB is about 5,100 ticks, roughly 720 microseconds or 3.6% of the frame, so blitter time alone caps a screen of such BOBs in the high twenties before the playfield, the CPU’s own chip-RAM accesses and game logic take their share. Dave Jones’s Menace aliens were 32×24 in three planes, which is why the game could afford a screenful of them.
Hybrid: hardware sprites alongside BOBs
Many games combine both: hardware sprites for the player (always visible, never flickers, automatic background priority) + BOBs for enemies, bullets, and items (unlimited count, full bitplane colour). The two approaches cooperate:
- Hardware sprites override BOBs at the pixel level via the playfield-vs-sprite priority bits in BPLCON2
- The hardware-sprite count budget (8 total, paired into 4 attached for 15-colour sprites) usually goes to the protagonist
- Everything else is BOB territory
Menace is the textbook layout — hardware sprites for the ship, BOBs for the aliens. Codetapper’s Sprite Tricks analyses document the opposite arrangement in later games: Risky Woods builds its 16-colour background layer from all eight hardware sprites repositioned across the screen, R-Type 2 uses the eight sprites for a 64-pixel repeating background pattern on most levels, and Jim Power draws its static background with two sprites repositioned across the line, keeping the rest for the main character, the status bar and player bullets. In each case the actors are BOBs and the sprites, freed from bookkeeping against the playfield, become scenery.