Amiga Machine Code Letter VI - Blitter 3
We have reached Letter VI of the Amiga Machine Code course. I will only skip through the details in this post. Make sure to read the letter, to get a grasp of things.
In this post I will go through the code of the mc0602 program, that’s found on Disk 1. I have annotated the code with a lot of comments, so that it should be possible to get a gist of things reading this post alone.
To get things running, you need to use the “ri” command of Seka to load the assets into memory at their designated labels. I got this demo running with 300 kb chip-mem in Seka. Remember that we want chip-mem, because that’s the only memory that the custom chips can read.
Open Seka and go to the folder where Disk 1 is. The book Amiga Machine Language contains some documentation on Seka if needed. Now write something along these lines:
SEKA>r
FILENAME>mc0602
SEKA>a
OPTIONS>
No Errors
SEKA>ri
FILENAME>screen
BEGIN>screen
END>
SEKA>ri
FILENAME>fig
BEGIN>fig
END>
SEKA>ri
FILENAME>mask
BEGIN>mask
END>
As is seen from the above, we have assets for the background screen and for the figure fig and it’s corresponding mask. We need to load these assets into memory at the designated labels for this demo to work. Let’s start with a screenshot 😃
This program builds upon the same general principals as the previous mc0601 program. But it uses many more subroutines to keep things organized.
The general gist, is that we now have 5 bitplanes and thus 32 colors, for both background and blitter object. The blitter object is moved on the screen, using the mouse.
We also use a more advanced blitter logic function, called the cookie-cut function, that we investigated in a previous post.
The shiftblit subroutine is also a bit interesting, so I will write a couple of notes on this in the buttom of this post.
move.w #$4000,$dff09a ; INTENA clr master interrupt
;----Stop disk drives
or.b #%10000000,$bfd100 ; set CIABPRB MTR
and.b #%10000111,$bfd100 ; clr CIABPRB SEL3, SEL2, SEL1, SEL0
move.w #$01a0,$dff096 ; DMACON clear bitplane, copper, blitter
;-----Setup bitplanes, display and DMA data fetch---
;-----Resolution 320*256 with 5 bitplanes
move.w #$5200,$dff100 ; BPLCON0 use 5 bitplanes (32 colors)
move.w #$0000,$dff102 ; BPLCON1 scroll
move.w #$0000,$dff104 ; BPLCON2 video
move.w #0,$dff108 ; BPL1MOD modulus odd planes
move.w #0,$dff10a ; BPL2MOD modulus even planes
move.w #$2c81,$dff08e ; DIWSTRT upper left corner ($81,$2c)
move.w #$f4c1,$dff090 ; DIWSTOP enaple PAL trick
move.w #$38c1,$dff090 ; DIWSTOP lower right corner ($1c1,$12c)
move.w #$0038,$dff092 ; DDFSTRT data fetch start at $38
move.w #$00d0,$dff094 ; DDFSTOP data fetch stop at $d0
;-----Transfer colors from screen to the color table registers
lea.l screen,a1 ; write screen address into a1
move.l #$dff180,a2 ; move address of COLOR00 into a2
moveq #31,d0 ; set color counter to 31
colorloop:
move.w (a1)+,(a2)+ ; move color from screen to color table
dbra d0,colorloop ; if not -1 then go to colorloop
;-----Set bitplane pointers in bplcop---
lea.l bplcop,a2 ; write bplcop address into a2
addq.l #2,a2 ; add two bytes so a2 can set BPL1PTH
move.l a1,d1 ; move a1 (points to screen data) into d1
moveq #4,d0 ; set bitplane counter to 4
bplcoploop:
swap d1 ; perform swap of words
move.w d1,(a2) ; move bit 0-15 into what a2 points to (sets BPLxPTH)
addq.l #4,a2 ; make a2 point to indput for PBLxPTL
swap d1 ; perform swap of words
move.w d1,(a2) ; move bit 0-15 into what a2 points to (sets BPLxPTL)
addq.l #4,a2 ; make a2 point to the next BPLxPTH input
add.l #10240,d1 ; make d1 point to next bitplane
dbra d0,bplcoploop ; decrement d0. if > -1 goto bplcoploop
;-----Start copper---
lea.l copper,a1 ; put address of copper into a1
move.l a1,$dff080 ; set COP1LCH and COP1LCL to address in a1
move.w $dff088,d0 ; start copper by read of strobe address COPJMP1
move.w #$8580,$dff096 ; DMACON set BLTPRI, PBLEN, COPEN
bsr readmouse ; read mouse coordinates to determine blit area
bsr storeback ; store background to blit in backbuffer
mainloop:
move.l $dff004,d0 ; read VPOSR and VHPOSR into d0 as one long word
asr.l #8,d0 ; shift right 8 poositions
and.l #$1ff,d0 ; and for immediate data
cmp.w #300,d0
bne mainloop ; if not at line 300 goto mainloop
bsr recallback ; recall blitted background from backbuffer
bsr readmouse ; read mouse coordinates to determine blit area
bsr storeback ; store background to blit in backbuffer
bsr shiftblit ; blit pixelwise horisontal
bsr blitin ; do the actual blit using cookie-cut
btst #6,$bfe001 ; CIAAPRA FIR0 check mouse button
bne mainloop ; if not pressed goto mainloop
move.w #$0080,$dff096 ; DMACON clear copper
move.l $4,a6 ; reestablish DMA's and copper
move.l 156(a6),a6
move.l 38(a6),$dff080
move.w #$80a0,$dff096
move.w #$0400,$dff096
move.w #$c000,$dff09a
rts ; return from mainloop
;-----blitin is blitting in the data from A, B, and C into D
;-----using the cookie-cut logic function
blitin:
lea.l maskbuffer,a1 ; store maskbuffer address in a1
lea.l backbuffer,a3 ; store backbuffer address in a3
lea.l figbuffer,a2 ; store figbuffer address in a2
lea.l screen,a4 ; store screen in a4
add.l #64,a4 ; skip first 64 bytes of color data
lea.l mousex,a0 ; store mousex address in a0
move.l (a0),d0 ; move mousex value into d0
lea.l mousey,a0 ; store mousey address in a0
move.l (a0),d1 ; move mousey value into d1
;-----find first blit position
lsr.l #4,d0 ; mouse x shift right 4 bits
lsl.l #1,d0 ; mouse x shift left 1 bit
mulu #40,d1 ; unsigned multiply to mousey (40 bytes is width of screen)
add.l d0,a4 ; add mousex to screen address in a1
add.l d1,a4 ; add mousey to a1
moveq #4,d7 ; initialize loop counter (5 bitplanes)
blitinloop:
btst #6,$dff002 ; wait for blitter
bne blitinloop
move.l a4,$dff054 ; BLTDPTH and BLTDPTL points to screen
move.l a1,$dff050 ; BLTAPTH and BLTAPTL points to maskbuffer
move.l a2,$dff04c ; BLTBTPH and BLTBPTL points to figbuffer
move.l a3,$dff048 ; BLTCPTH and BLTAPTL points to backbuffer
move.w #32,$dff066 ; BLTDMOD set modulus to 32 bytes on D 40-(64/8)
move.w #0,$dff064 ; BLTAMOD set modulus to 0 bytes on A
move.w #0,$dff062 ; BLTBMOD set modulus to 0 bytes on B
move.w #0,$dff060 ; BLTCMOD set modulus to 0 bytes on C
move.l #$ffffffff,$dff044 ; set BLTAFWM/BLTALWM first and last word mask for A
move.w #$0fca,$dff040 ; BLITCON0 use A,B,C, and D, with cookie-cut
move.w #$0000,$dff042 ; BLITCON1
move.w #$0b44,$dff058 ; set BLTSIZE height 45 lines, width 4 words (64 pixels)
add.l #360,a2 ; point to next bitpane in figbuffer
add.l #360,a3 ; point to next bitplane in backbuffer
add.l #10240,a4 ; point to next bitplane in screen
dbra d7,blitinloop ; if d7 > -1 goto blitinloop
rts ; return from blitin
;----shiftblit enables us to blit
;----pixelwise instead of wordwise horizontal
shiftblit:
lea.l fig,a1 ; put fig address into a1
lea.l figbuffer,a2 ; put figbuffer address into a2
lea.l mousex,a0 ; put mousex address into a0
move.l (a0),d1 ; put mousex value into d1
;-----preparing a value for BLTCON0 by first setting up
;-----the shift value (byte 12-15) and then use A and D
;-----with the logic function D=A
andi.l #$f,d1 ; clear all but first byte of mousex in d1
lsl.l #8,d1 ; shift left 8 bits (max allowed)
lsl.l #4,d1 ; shift left another 4 bits
add.w #$09f0,d1 ; value for using A and D with logic function D=A
moveq #4,d7 ; intialize loop counter (5 bitplanes)
shiftfigloop:
btst #6,$dff002 ; wait for blitter to finish
bne shiftfigloop
move.l a2,$dff054 ; set BLTDPTH and BLTDPTL to figbuffer
move.l a1,$dff050 ; set BLTAPTH and BLTAPTL to fig
move.w #0,$dff066 ; set BLTDMOD modulus to 0 bytes on D
move.w #0,$dff064 ; set BLTAMOD modulus to 0 bytes on A
move.l #$ffffffff,$dff044 ; set BLTAFWM/BLTALWM first and last word mask for A
move.w d1,$dff040 ; BLTCON0 see above for settings
move.w #$0000,$dff042 ; BLTCON1
move.w #$0b44,$dff058 ; set BLTSIZE height 45 lines, width 4 words (64 pixels)
add.l #360,a1 ; point to next bitplane in fig
add.l #360,a2 ; point to next bitplane in figbuffer
dbra d7,shiftfigloop ; if d7 > -1 goto shiftfigloop
lea.l mask,a1 ; put mask address into a1
lea.l maskbuffer,a2 ; put maskbuffer address into a2
shiftmaskloop:
btst #6,$dff002 ; wait for blitter (BLTSIZE triggers the blitter)
bne shiftmaskloop
move.l a2,$dff054 ; set BLTDPTH and BLTDPTL to maskbuffer
move.l a1,$dff050 ; set BLTAPTH and BLTAPTL to mask
move.w #0,$dff066 ; set BLTDMOD modulus to 0 bytes on D
move.w #0,$dff064 ; set BLTAMOD modulus to 0 bytes on A
move.l #$ffffffff,$dff044 ; set BLTAFWM/BLTALWM first and last word mask for A
move.w d1,$dff040 ; BLTCON0 see above for settings
move.w #$0000,$dff042 ; BLTCON1
move.w #$0b44,$dff058 ; set BLTSIZE height 45 lines, width 4 words 64 pixels
rts ; return from shiftblit
;-----subroutine read mouse x, y---
;-----store result in mousex and mousey---
readmouse:
move.w $dff00a,d0 ; move JOY0DAT into d0
move.l d0,d1 ; move d0 value into d1
lsr.w #8,d1 ; shift right 8 bits
andi.l #$ff,d0 ; clean with and - d0 holds mouse x value
andi.l #$ff,d1 ; clean with and - d1 holds mouse y value
lea.l mousex,a1 ; store mousex result address into a1
move.l d0,(a1) ; write mouse x value into result address
lea.l mousey,a1 ; same stuff for mouse y
move.l d1,(a1)
rts ; return from readmouse
;-----Store screen in backbuffer---
storeback:
lea.l screen,a1 ; store screen address in a1
add.l #64,a1 ; move address past color data
lea.l backbuffer,a2 ; store backbuffer address in a2
lea.l mousex,a0 ; store mousex address in a0
move.l (a0),d0 ; move mouse x value into d0
lea.l mousey,a0 ; store mousey address in a0
move.l (a0),d1 ; move mouse y value into d1
;-----find first blit position
lsr.l #4,d0 ; mouse x shift right 4 bits
lsl.l #1,d0 ; mouse x shift left 1 bit
mulu #40,d1 ; unsigned multiply to mousey (40 bytes is width of screen)
add.l d0,a1 ; add mousex to screen address in a1
add.l d1,a1 ; add mousey to a1
moveq #4,d7 ; initializer loop counter (5 bitplanes)
storebackloop:
btst #6,$dff002 ; wait for blitter
bne storebackloop
move.l a2,$dff054 ; set BLTDPTH and BLTDPTL to backbuffer
move.l a1,$dff050 ; set BLTAPTH and BLTAPTL to mouse pos on screen
move.w #0,$dff066 ; set BLTDMOD modulus to 0 bytes on D
move.w #32,$dff064 ; set BLTAMOD modulus to 32 bytes on A
move.l #$ffffffff,$dff044 ; set BLTAFWM/BLTALWM first and last word mask for A
move.w #$09f0,$dff040 ; BLTCON0 use A and D, set logic function D=A
move.w #$0000,$dff042 ; BLTCON1
move.w #$0b44,$dff058 ; set BLTSIZE height 45 lines, width 4 words 64 pixels
add.l #10240,a1 ; point to next bitplane in screen
add.l #360,a2 ; point to next bitplane in backbuffer (45 * 8 bytes)
dbra d7,storebackloop ; if d7 > -1 goto storebackloop
rts ; return from storeback
;-----Write backbuffer to screen
recallback:
lea.l screen,a1 ; store screen address in a1
add.l #64,a1 ; move address past color data
lea.l backbuffer,a2 ; store backbuffer address in a2
lea.l mousex,a0 ; store address of mousex in a0
move.l (a0),d0 ; move mousex value into d0
lea.l mousey,a0 ; store address of mousey in a0
move.l (a0),d1 ; move mousey value into d1
;-----find first blit position
lsr.l #4,d0 ; mouse x is shifted 4 bits right
lsl.l #1,d0 ; mouse x is shifted 1 bit left
mulu #40,d1 ; unsigned multiply to mousey (40 bytes is width of screen)
add.l d0,a1 ; add mousex to screen address in a1
add.l d1,a1 ; add mousey to a1
moveq #4,d7 ; initialize counter for the loop (5 bitplanes)
recallbackloop:
btst #6,$dff002 ; wait for blitter
bne recallbackloop
move.l a1,$dff054 ; set BLTDPTH and BLTDPTL to mouse pos on screen
move.l a2,$dff050 ; set BLTAPTH and BLTAPTL to backbuffer
move.w #32,$dff066 ; set BLTDMOD modulus to 32 bytes on D
move.w #0,$dff064 ; set BLTAMOD modulus to 0 bytes on A
move.l #$ffffffff,$dff044 ; set BLTAFWM/BLTALWM mask for A
move.w #$09f0,$dff040 ; BLTCON0 use A and D, set logic function D=A
move.w #$0000,$dff042 ; BLTCON1
move.w #$0b44,$dff058 ; set BLTSIZE height 45 lines, width 4 words 64 pixels
add.l #10240,a1 ; point to next bitplane in screen
add.l #360,a2 ; point to next bitplane in backbuffer (45 * 8 bytes)
dbra d7,recallbackloop ; if d7 > -1 goto recallbackloop
rts ; return from recallback
copper:
dc.w $2c01,$fffe ; wait($01,$2c)
dc.w $0100,$5200 ; (move) set BPLCON0 use 5 bitplanes, enable color burst
bplcop:
dc.w $00e0,$0000 ; BPL1PTH
dc.w $00e2,$0000 ; BPL1PTL
dc.w $00e4,$0000 ; BPL2PTH
dc.w $00e6,$0000 ; BPL2PTL
dc.w $00e8,$0000 ; BPL3PTH
dc.w $00ea,$0000 ; BPL3PTL
dc.w $00ec,$0000 ; BPL4PTH
dc.w $00ee,$0000 ; BPL4PTL
dc.w $00f0,$0000 ; BPL5PTH
dc.w $00f2,$0000 ; BPL5PTL
dc.w $ffdf,$fffe ; wait($df,$ff) enable wait < $ff horiz
dc.w $2c01,$fffe ; wait($01,$12c) for PAL
dc.w $0100,$0200 ; (move) set BPLCON0 disable bitplanes
; needed to support older PAL chips
dc.w $ffff,$fffe ; end of copper
screen:
blk.l 12816,0 ; allocate 64 + 320/8*256*5 = 51264 bytes = 12816 longs
fig:
blk.l 450,0 ; 45 lines * 64 pixels * 5 bitplanes = 14400 bits = 450 longs
mask:
blk.l 90,0 ; allocate 45 lines * 64 pixels = 2880 bits = 90 longs
figbuffer:
blk.l 450,0
maskbuffer:
blk.l 90,0
backbuffer:
blk.l 450,0
mousex:
dc.l 0
mousey:
dc.l 0
Shiftblit subroutine
Up until now, we have blitted an object that scrolled vertically down the screen. This simple animation, was achived by moving the start address of the blit down the screen.
However, since we are now using the mouse as movement input, we are forced to also consider horizontal movement. This poses a problem, because the start of the blit is always given as an adress. If we left it at that, we would get some very clunky movement, moving the blitter object at words at a time.
What we want, is to move the blitter object by blitting a few pixels and not whole words. To accomblish that, we use the shift values in BLTCON0, which is stored in the d1 register in the shiftblit subroutine.
To read more about pixelwise horizontal movement, take a look at the Amiga Hardware Reference Manaual. They have an example of an animated car driving down the road.
Also, if you have some troubles understanding the differences between logic and arithmetic shift operations, like LSL and ASL, then take a look at this youtube video by Padraic Edgington.
One reader wrote, that he had a little trouble with blitting something else than a donut 🍩. Fortunatly it can be done - read about it here:
Previous post: Amiga Machine Code Letter VI - Blitter 2
Next post: Amiga Machine Code Letter VII - Blitting and Scrolling.