Writting the Letter II posts, left me with a feeling that many details had been left unanswered. So it was with mixed feelings that I started reading Letter III.
The letter turned out to be a good read, answering many of the questions I had from the previous letter. Mind you, that Letter III is a long one, that covers a lot of ground. So we better get started 😉.
Boolean arithmatic and truth tables are introduced, and I won’t go through it here, because this is really basic stuff. However there are two concepts, that are simple, and yet very important for setting and clearing bitfields. We are going to do this a lot, when dealing the the Amiga custom chip registers.
Unset the 4th bit Register value: %0110 1011 AND to unset: %1111 0111 ---------- Result: %0110 0011 ========== Set the 4th bit Register value: %0110 0011 OR to set: %0000 1000 ---------- Result: %0110 1011 ==========
Copper instructions revisited
Letter III goes into more depth about the move and wait copper instructions. The skip instruction is skipped, because it’s not that commenly used. The Amiga Hardware Manual has a table of the instructions, that is also repeated in the letter.
Notice how bit 0 in both instruction words (IR1, IR2) are used to identify the move, wait, and skip instruction. I have highlighted this with the green box.
The move instruction is identified by bit 0 in IR1 being 0, which means that we can only move to an even destination address. Fortunatly this is OK, since the custom chip registers are 16 bit or a word in width. A word is two bytes which means that all registers are accessed through even addresses. Phew…
However, we meet a limitation regarding the wait instruction. We can only wait until the horizontal beam counter hits an uneaven number. The vertical and horizontal masks are also limitied, since they only are repsented by 7 bits each.
There is a mnemonic for the wait and skip instructions, that makes it easier to remember what they do. Simply replace wait with “wait until”, and skip with “skip after”.
Copper and the chip registers
The custom chip registers are not registers in the CPU, like the Address and Data registers. They are registers outside the CPU accessed through memory addresses from $DFF000 to $DFF200.
The copper move instruction has a destination field that is not 24 bit wide as addresses are, but only 8 bit wide. Still move can write to all chip registers, because it’s input destination is given as an offset to $DFF000.
Some of the registers can be written to, while others can only be read. If both read and write are needed, then this can be done by using two registers.
The DMACON uses bit 15 to indicate if a set or clear is given by the input. If we want to clear (or turn off) the bitplane and sprite DMA’s then we have to clear bit 5 and 8. Remember to start counting from 0.
move.w %0000 0001 0010 0000, $DFF096 move.w $0120, $DFF096 ; same as above just using hex
To enable the bitplane and sprite DMA’s just repeat the above with a 1 at bit 15.
move.w %1000 0001 0010 0000, $DFF096 move.w $8120, $DFF096 ; same as above just using hex
How simple is that! 😃
Let’s revisit a code listing from Letter II, now with some more comments.
start: move.w #$01A0, $DFF096 ; disable sprite, copper, and bitplane DMA's lea.l copperlist, A1 ; put the address of the copperlist into a1 move.l A1, $DFF080 ; move data in a1 into the copper first location register move.w #$8080, $DFF096 ; enable copper DMA wait: btst #6, $BFE001 ; busy wait until left mouse is pressed bne wait move.w #$0080, $DFF096 ; disable the copper DMA move.l $04, A6 ; ? ... Something with bringing back the workbench move.l 156(A6), A1 ; ? ... move.l 38(A1), $DFF080 ; ? ... move.w #$81A0, $DFF096 ; enable sprite, copper and bitplane DMA's rts ; return from subroutine, go back to the call site copperlist: dc.w $9001, $FFFE ; wait for line 144 dc.w $0180, $0F00 ; move red color to $DFF180 dc.w $A001, $FFFE ; wait for line 160 dc.w $0180, $0FFF ; move white color to $DFF180 dc.w $A401, $FFFE ; wait for line 164 dc.w $0180, $000F ; move blue color to $DFF180 dc.w $AA01, $FFFE ; wait for line 170 dc.w $0180, $0FFF ; move white color to $DFF180 dc.w $AE01, $FFFE ; wait for line 174 dc.w $0180, $0F00 ; move red color to $DFF180 dc.w $BE01, $FFFE ; wait for line 190 dc.w $0180, $0000 ; move black color to $DFF180 dc.w $FFFF, $FFFE ; end of copper list
Notice that we use a move.l to move a long to $DFF080. This should not be possible, since the chip registers are only one word wide. However, what happens is that both $DFF080 and $DFF082 are written to. These are also callled COP1LCH and COP1LCL. To store a pointer to a memory address (the copperlist) which is 24 bit wide, we have to use two registers.
The letter finishes with a litte teaser program about bitplanes. I call it a teaser, because it’s only superfically explained. It seems like it was just added to give people something to code, while delegating the in-depth explaination to a later letter.
Allright, we’re going to stop here for now. Next post will be about branching