Amiga Machine Code Letter V - Sprites
We have reached Letter V of the Amiga Machine Code course.
The program examples are now so big, that we have to read them from the course disk Disk 1
Below is a listing of the program mc0501 that shows a sprite of a flag of Norway, that scrolls down the screen. I have annotated the program and below the listing I will dive into a couple of details.
; mc0501
move.w #$4000,$dff09a ; INTENA - clear external interrupt
or.b #%10000000,$bfd100 ; CIABPRB stops drive motors
and.b #%10000111,$bfd100 ; CIABPRB
move.w #$01a0,$dff096 ; DMACON clear bitplane, copper, sprite
move.w #$1200,$dff100 ; BPLCON0 one bitplane, color burst
move.w #$0000,$dff102 ; BPLCON1 scroll
move.w #$003f,$dff104 ; BPLCON2 video
move.w #0,$dff108 ; BPL1MOD bitplane modulo odd planes
move.w #0,$dff10a ; BPL2MOD bitplane modulo even planes
move.w #$2c81,$dff08e ; DIWSTRT upper left corner of display ($81,$2c)
move.w #$f4c1,$dff090 ; DIWSTOP enable PAL trick
move.w #$38c1,$dff090 ; DIWSTOP lower right corner of display ($1c1,$12c)
move.w #$0038,$dff092 ; DDFSTRT Data fetch start
move.w #$00d0,$dff094 ; DDFSTOP Data fetch stop
lea.l sprite,a1 ; put sprite address into a1
lea.l copper,a2 ; put copper address into a2
move.l a1,d1 ; move sprite address into d1
move.w d1,6(a2) ; transfer sprite address high to copper
swap d1 ; swap
move.w d1,2(a2) ; transfer sprite address low to copper
lea.l blanksprite,a1 ; put blanksprite address into a1
lea.l copper,a2 ; put copper address into a2
add.l #10,a2 ; add 10 to copper address in a2
move.l a1,d1 ; move blanksprite address into d1
moveq #6,d0 ; setup sprite counter
sprcoploop: ; set all 7 sprite pointers
swap d1 ; high and low to point to blanksprite
move.w d1,(a2)
addq.l #4,a2
swap d1
move.w d1,(a2)
addq.l #4,a2
dbra d0,sprcoploop ; loop trough all 7 sprite pointers
lea.l screen,a1 ; put screen address into a1
lea.l bplcop,a2 ; put bplcop address into a2
move.l a1,d1 ; transfer screen address to bplcop
move.w d1,6(a2)
swap d1
move.w d1,2(a2)
lea.l copper,a1 ; put copper address into a1
move.l a1,$dff080 ; COP1LCH (also sets COP1LCL)
move.w $dff088,d0 ; COPJMP1
move.w #$81a0,$dff096 ; DMACON set bitplane, copper, sprite
wait: ; wait until at beam line 0
move.l $dff004,d0 ; read VPOSR and VHPOSR into d0 as one long word
asr.l #8,d0 ; shift right 8 places
and.l #$1ff,d0
cmp.w #0,d0
bne wait ; if not equal jump to wait
wait2: ; wait until at beam line 1
move.l $dff004,d0 ; read VPOSR and VHPOSR into d0 as one long word
asr.l #8,d0
and.l #$1ff,d0
cmp.w #1,d0
bne wait2 ; if not equal jump to wait
bsr movesprite ; branch to subroutine movesprite
btst #6,$bfe001 ; test left mouse left mouse click
bne wait ; if not pressed jump to wait
move.w #$0080,$dff096 ; reestablish DMA's and copper
move.l $04,a6
move.l 156(a6),a1
move.l 38(a1),$dff080
move.w #$8080,$dff096
move.w #$c000,$dff09a
rts
movesprite: ; movesprite subroutine
lea.l sprite,a1
cmp.b #250,2(a1) ; sprite bottom line at 250
bne notbottom ; if not go to notbottom
move.b #30,(a1)
move.b #44,2(a1)
notbottom:
add.b #1,(a1) ; move sprite top line by 1
add.b #1,2(a1) ; move sprite bottom line by 1
rts ; return from subroutine
copper:
dc.w $0120,$0000 ; SPR0PTH
dc.w $0122,$0000 ; SPR0PTL
dc.w $0124,$0000 ; SPR1PTH
dc.w $0126,$0000 ; SPR1PTL
dc.w $0128,$0000 ; SPR2PTH
dc.w $012a,$0000 ; SPR2PTL
dc.w $012c,$0000 ; SPR3PTH
dc.w $012e,$0000 ; SPR3PTL
dc.w $0130,$0000 ; SPR4PTH
dc.w $0132,$0000 ; SPR4PTL
dc.w $0134,$0000 ; SPR5PTH
dc.w $0136,$0000 ; SPR5PTL
dc.w $0138,$0000 ; SPR6PTH
dc.w $013a,$0000 ; SPR6PTL
dc.w $013c,$0000 ; SPR7PTH
dc.w $013e,$0000 ; SPR7PTL
dc.w $2c01,$fffe
dc.w $0100,$1200
bplcop:
dc.w $00e0,$0000 ; BPL1PTH
dc.w $00e2,$0000 ; BPL1PTL
dc.w $0180,$0000 ; COLOR00 black
dc.w $0182,$0ff0 ; COLOR01 yellow
dc.w $01a2,$0f00 ; COLOR17 sprite0 red
dc.w $01a4,$0fff ; COLOR18 sprite0 white
dc.w $01a6,$000b ; COLOR19 sprite0 blue
dc.w $ffdf,$fffe ; wait($df,$ff) enables waits > $ff vertical
dc.w $2c01,$fffe ; wait($01,$12c) - $2c is $12c
dc.w $0100,$0200 ; BPLCON0 unset bitplanes, enable color burst
; needed to support older PAL chips
dc.w $ffff,$fffe ; end of copper
screen:
blk.b 10240,0 ; allocate 1 kb of memory and set it to zero
sprite:
dc.w $1e8c,$2c00
dc.w $FB7F,$0780 ; %1111 1011 0111 1111, %0000 0111 1000 0000
dc.w $FB7F,$0780 ; %1111 1011 0111 1111, %0000 0111 1000 0000
dc.w $FB7F,$0780 ; %1111 1011 0111 1111, %0000 0111 1000 0000
dc.w $FB7F,$0780 ; %1111 1011 0111 1111, %0000 0111 1000 0000
dc.w $FB7F,$0780 ; %1111 1011 0111 1111, %0000 0111 1000 0000
dc.w $0300,$FFFF ; %0000 0011 0000 0000, %1111 1111 1111 1111
dc.w $FFFF,$FFFF ; %1111 1111 1111 1111, %1111 1111 1111 1111
dc.w $FFFF,$FFFF ; %1111 1111 1111 1111, %1111 1111 1111 1111
dc.w $0300,$FFFF ; %0000 0011 0000 0000, %1111 1111 1111 1111
dc.w $FB7F,$0780 ; %1111 1011 0111 1111, %0000 0111 1000 0000
dc.w $FB7F,$0780 ; %1111 1011 0111 1111, %0000 0111 1000 0000
dc.w $FB7F,$0780 ; %1111 1011 0111 1111, %0000 0111 1000 0000
dc.w $FB7F,$0780 ; %1111 1011 0111 1111, %0000 0111 1000 0000
dc.w $FB7F,$0780 ; %1111 1011 0111 1111, %0000 0111 1000 0000
dc.w $0000,$0000 ; %0000 0000 0000 0000, %0000 0000 0000 0000
dc.w $0000,$0000 ; %0000 0000 0000 0000, %0000 0000 0000 0000
blanksprite:
dc.w $0000,$0000 ; an empty sprite
The sprite it self is defined as 16 pixels in width, while you define the length. A sprite can have four colors, one of which is a transparent color, and can be placed at any position on the screen. The first color register in the group of four color registers is ignored and thus is the transparent color.
A sprite is build by a series of longs.
long 1 ; position and height
long 2 ; sprite data
long 3 ; sprite data
...
long N ; zeros denotes the last line of the sprite.
The first long of a sprite defines the position and height of the sprite. It’s a little bit convoluted becasue we can’t fit any screen position into 8 bits. Here’s a schematic.
The schematic shows that the sprite starts at line $1e and ends at line $2c. The horizontal position of the left side starts at $8c * 2 = $118. We multiply with two because the first control bit is bit 0.
The sprite graphics data works the same way as bitplanes. Each line of sprite graphics data takes two words, where each word defines a bitplane of 16 pixels width. With this setup we can have four colors.
The color register assignments can be seen in the Amiga Hardware Reference Manual.
Sprite wave
The next example in the letter is mc0502, which is also found on Disk 1. It is twice as long as the above program, so I won’t list it here. It’s also almost similar in principle to the first example.
However, the mc0502 program has more sprites and a background image. The sprites move according to a pattern defined in a table. Both background and table should be loaded into Seka.
SEKA>ri
FILENAME>screen
BEGIN>screen
END>
SEKA>ri
FILENAME>movetable
BEGIN>movetable
END>
Here’s a screenshot.The sprites spells “letter course” in norwegian.
The movement pattern is stored in a table, because it’s quite expensive to calculate.
Trigonomic functions like sine and cosine are also non-trivial to implement in assembler. Hence, there grew a need for wave generator programs, that could produce such tables. One such program is called wavegen and can be found on Disk 1.
These days, we all have access to floating point hardware, so we don’t see the lookup table optimization technique so much anymore. Functions like sine and cosine has the interesting property of being pure functions. That is, a function that given the same input produces the same output. E.g. sine to 90 degrees is always the same. Such class of functions can be completely replaced by table lookups.
There’s an interesing note about using tables to store movements over at coppershade.org. Go and take a look, it’s a fantastic site.
It’s expensive to calculate sine and cosine, but not impossible. Many used an algorithm from 1959 called CORDIC or Volder’s algorithm that only relied on addition, subtraction, bitshift and table lookups. This algorithm has found it’s way into many chips, includning the floating point coprocessors from Motorola, the 68881 and 68882.
Previous post: Amiga Machine Code Letter V
Next post: Amiga Machine Code Letter VI - Blitter.