Amiga Machine Code Letter III - Copper Revisited

Amiga Machine Code - Letter III

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 with 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.

Copper instructions

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 represented 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.

One example of such double register is the DMA control. Writing to DMACON is done through $DFF096, and reading is done via DMACONR at $DFF002.

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.

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

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

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

Amiga Machine Code Course

Previous post: Amiga Machine Code Letter II - Part 2

Next post: Amiga Machine Code Letter III - Branching.

Mark Wrobel
Mark Wrobel
Team Lead, developer and mortgage expert