Amiga Machine Code Letter VII - Blitting and Scrolling
We have reached Letter VII of the Amiga Machine Code course. It took a while getting here 😉.
In this post, we are going to take another round with the blitter. This time to show how to make scrolling work! There’s a few concepts that are a bit hard to grasp, but the letter does a great job at explaining them. However, there some darker spots, so lets dive in!
Up until now, we have only used the the blitter in ascending mode. We have blitted seperate areas of memory into the destination - the screen memory.
This time, however, we are going to blit from the screen to the screen. If the source and destination overlaps, we risk overwritting data, before the blitter has had the chance to copy it to the destination.
In the book Mapping the Amiga, there’s a simple example of how such a data overwrite could happen.
For example, say memory location 102 holds a $\$FEDC$ and memory location 104 holds a $\$BA98$. If you want to copy these two words into locations 104 and 106, the blitter would first take the $\$FEDC$ from 102 and move it into 104. Then, when it want to move the word in 104, it would find $\$FEDC$, not $\$BA98$, because that word had been destroyed by the first move operation.
To avoid loss of data, the blitter can run in descending mode, where it works in reverse. It starts at the end and works it way back to the beginning of the source and destination. Descending mode is turned on by setting bit 1 of the BLTCON1 register.
Let’s look at the example again, now with descending mode turned on.
Start with the last word of the source, and blit it to the last word of the destination.
Proceed backwards until the beginning of the source is reached, and blit it to the first word of the destination.
Voila! No source data was lost 👍.
Mapping the Amiga, provides the following rule for choosing between ascending and descending mode.
If the end of the source overlaps the start of the destination, use descending mode. If the start of the source overlaps the end of the destination, use ascending mode. If the source and destination don’t overlap at all, use either mode you please.
In the image below, the end of the source overlaps the start of the destination. In this case use descending mode.
In the image below, the start of the source overlaps the end of the destination. In this case use ascending mode.
In descending mode, the modulo of BLTxMOD will be subtracted rather than added. Also the shifts will work in reverse, so that the shifts defined in BLTCON0 and BLTCON1 will shift to the left, rather than to the right.
As we shall see later, shifting to the left makes text scrolling from right to left possible.
A simple text scroller
The first code listing, mc0701, in Letter VII, shows how to make a simple text scroller. The code is organized by using subroutines, but every now and then the occasional magic number pops up, which make the code a little bit hard to read.
The letter does a good job of explaining the code, so I will only add explainations to what i found a little murky.
Let’s start out with a screenshot of the program in action.
What the font!
The characters in the font are all 20 lines high and 16 pixels wide. However since not every character has equal width, we get some rather unattractive gabs in the text spacing. Just look at the gab between “I” and “G” when spelling “AMIGA” in the screenshot above.
One of the exercises in the letter, is to introduce character dependent font width 😉.
The font is stored in a binary file, called font, and it contains 32 characters, including comma, punctuation and space. The font is stored as a bitmap of 512 * 20 in one bitplane.
The screenshot of the font asset is made by IFFCon. The program can be found on disk1 in the c folder.
Start IFFCon from the CLI. In IFFCon press right mouse and choose load bitmap from the projects menu. In the pop-up type in 512 as width, 20 as height, and 1 bitplane. Location of asset on my system is amigahd:disk1/brev07/font
Make it scroll
The scrolling is done by transfering the characters into memory, and then shift-blitting them to the left. Hence, we need to blit in descending mode, to get the text to scroll from right to left.
The screen memory is setup in a way we haven’t seen before. The visible screen is 22 words times 256 lines i.e. 353 * 256 pixels. We set the bitplane modulo to 2, so that each line will have 16 pixels at the end, that are not visible. It’s here we place the characters to be scrolled, and then left-shift them into the visible screen. This will make it look like the characters flow into the screen.
In the code, we have some magic numbers. Two of them are point A and B, shown in the figure above.
Point A defines the offset from the start of the screen, where the graphics characters from the font should be written. The characters are all 20 lines high and 16 pixels wide (1 word). We start writting the character at the 150th line at the start of word 23 (byte 44).
$$Point A: 46 * 150 + 44 = 6944$$
Because the blitter is set to decending mode, we have to provide it with a pointer to the last address to be blitted. Point B defines the offset from the start of the screen, and is set at the end of the last line where we placed the character.
$$Point B: 46 * (150 + 20) = 7820$$
The program, mc0701, is rather simple, once the above is understood. Here’s the source code
move.w #$4000,$dff09a ; INTENA clear master interupt ;-----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 352*256 with 1 bitplane move.w #$1200,$dff100 ; BPLCON0 use 1 bitplanes (2 colors) move.w #$0000,$dff102 ; BPLCON1 scroll move.w #$0000,$dff104 ; BPLCON2 video move.w #$0002,$dff108 ; BPL1MOD modulus odd planes move.w #$0002,$dff10a ; BPL2MOD modulus even planes move.w #$2c71,$dff08e ; DIWSTRT upper left corner ($71,$2c) move.w #$f4d1,$dff090 ; DIWSTOP enaple PAL trick move.w #$38d1,$dff090 ; DIWSTOP lower right corner ($1d1,$12c) move.w #$0030,$dff092 ; DDFSTRT data fetch start at $30 move.w #$00d8,$dff094 ; DDFSTOP data fetch stop at $d8 ;-----set BPL1PTH/BPL1TPL in bplcop--- lea.l screen,a1 ; write screen address into a1 lea.l bplcop,a2 ; write bplcop address into a2 move.l a1,d1 ; move a1 to d1 swap d1 ; swap words move.w d1,2(a2) ; write first word into a2+2 (BPL1PTH) swap d1 ; swap words move.w d1,6(a2) ; write first word into a2+6 (BPL1PTL) ;-----setup 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 #$8180,$dff096 ; DMACON set PBLEN, COPEN 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 scroll ; scroll letters btst #6,$bfe001 ; CIAAPRA FIR0 check mouse button bne mainloop ; if not pressed goto mainloop move.w #$0080,$dff096 ; DMACON clear copper ;-----reestablish DMA's and copper--- move.l $04,a6 move.l 156(a6),a6 move.l 38(a6),$dff080 move.w #$80a0,$dff096 move.w #$c000,$dff09a rts ; return from mainloop scrollcnt: dc.w $0000 charcnt: dc.w $0000 ;-----scroll subroutine--- scroll: lea.l scrollcnt,a1 ; move scrollcnt address into a1 cmp.w #8,(a1) ; compare scrollcnt with 8 bne nochar ; if not equal goto nochar clr.w (a1) ; set scrollcnt to 0 lea.l charcnt,a1 ; move charcnt address into a1 move.w (a1),d1 ; move charcnt value into d1 addq.w #1,(a1) ; add 1 to charcnt value - d1 unaffected lea.l text,a2 ; move text address into a2 clr.l d2 ; set d2 to 0 - d2 points to current char move.b (a2,d1.w),d2 ; move value in address text+charcnt into d2 cmp.b #42,d2 ; check if d2 equals 42 (termination sign "*") bne notend ; if not equal goto notend clr.w (a1) ; set charcnt to 0 move.b #32,d2 ; move 32 into d2 (space " " = 32) notend: lea.l convtab,a1 ; move address of char convertion table into a1 move.b (a1,d2.b),d2 ; d2 is an offset in the table. Store result in d2 asl.w #1,d2 ; multiply d2 by two - font is 2 bytes wide - 16 pixels lea.l font,a1 ; move font address into a1 add.l d2,a1 ; add offset d2 to a1 so it points to current letter lea.l screen,a2 ; move screen address into a2 add.l #6944,a2 ; 46 * 150 + 44 moveq #19,d0 ; use d0 as counter. Font is 20 lines heigh putcharloop: ; loop over each horiz line in font move.w (a1),(a2) ; move 16 pixels of current letter into a2 add.l #64,a1 ; go to next line in current letter font add.l #46,a2 ; go to the next line on screen dbra d0,putcharloop ; if d0 > -1 goto putcharloop nochar: btst #6,$dff002 ; DMACONR test bit 6 BLTEN bne nochar ; if blitter enabled goto nochar lea.l screen,a1 ; move screen address into a1 add.l #7820,a1 ; add 46*(150+20) end of line 170 ; setup blitter move.l a1,$dff050 ; BLTAPTH and BLTAPTL set to end of line 170 move.l a1,$dff054 ; BLTDPTH and BLTDPTL set to end of line 170 move.w #0,$dff064 ; BLTAMOD set modulo to 0 bytes on A move.w #0,$dff066 ; BLTDMOD set modulo to 0 bytes on D move.l #$ffffffff,$dff044 ; set BLTAFWM first word mask for A move.w #$29f0,$dff040 ; BLTCON0 shift two bits on A, use A,D with D=A move.w #$0002,$dff042 ; BLTCON1 enable decending mode move.w #$0517,$dff058 ; BLTSIZE height 20 lines, width 23 words. 20 * 64 + 23 lea.l scrollcnt,a1 ; move scrollcnt address into a1 addq.w #1,(a1) ; add 1 to scrollcnt value rts ; return from scroll subroutine copper: dc.w $2c01,$fffe ; wait($01,$2c) dc.w $0100,$1200 ; BPLCON0 use 1 bitplane, enable color burst 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 $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 $b80,0 font: blk.l $140,0 convtab: dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $1f ;" " dc.b $00 dc.b $00 dc.b $1b ;Ø dc.b $1c ;Å dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $1d ;, dc.b $00 ;- dc.b $1e ;. dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $00 dc.b $1a ;Æ dc.b $00 ;A dc.b $01 ;B dc.b $02 ;C dc.b $03 ;... dc.b $04 dc.b $05 dc.b $06 dc.b $07 dc.b $08 dc.b $09 dc.b $0a dc.b $0b dc.b $0c dc.b $0d dc.b $0e dc.b $0f dc.b $10 dc.b $11 dc.b $12 dc.b $13 dc.b $14 dc.b $15 dc.b $16 ;.... dc.b $17 ;X dc.b $18 ;Y dc.b $19 ;Z dc.b $00 dc.b $00 dc.b $00 text: dc.b "DETTE ER EN TEST AV EN SCROLL P$ AMIGA.... *"
Previous post: Amiga Machine Code Letter VI - Blitter 3
Next post: Amiga Machine Code Letter VII - Colorcycling.