Amiga Agnus Copper Co-Processor

Agnus's robot friend.

WAIT

Horizontal Position

  • Horizontal WAITs are based on 4 pixel (in low res) blocks
  • The electron gun X position is represented with a value from $00 to $E2
    • Multiply this by 2 to get the X position on the screen
    • The Copper doesn't use the LSB in comparisons, so in reality you can only WAIT on horizontal positions that are even numbers
    • $01 is electron gun X position 2, but the Copper can't wait for this position. You can wait for $02, though.
    • $10 is electron gun X position 64. You can wait for this address.
  • The way that HBLANK is described in the original docs is not friendly to my brain:
    • Horizontal Blank goes from $35 on this line → $0F on the next line
    • The unused portion where only COLOR00 is displayed is $47 on this line → $04 on the next line

Examples

Reserving RAM for a new Copperlist

snippet.asm
  SECTION ChipRAM,Data_c
 
  CNOP 0,4
Copperlist
  DC.L $FFFFFFFE ; impossible condition = end of copperlist
  DCB.W 1024,0 ; 2048 more bytes for copper data

Macro to add bitplane to copperlist

snippet.asm
AddBitplaneToCopper MACRO
  MOVE.L \1,D0
  MOVE.W #BPL\2PTL,(A5)+
  MOVE.W D0,(A5)+
  SWAP   D0
  MOVE.W #BPL\2PTH,(A5)+
  MOVE.W D0,(A5)+
  ENDM
 
  AddBitplaneToCopper #Bitplanes,1
  AddBitplaneToCopper #Bitplanes+(320/8)*256,2
  AddBitplaneToCopper #Bitplanes+(320/8)*256*2,3
  AddBitplaneToCopper #Bitplanes+(320/8)*256*3,4
  AddBitplaneToCopper #Bitplanes+(320/8)*256*4,5

Building a copperlist dynamically in C

snippet.c
void *copperlist = AllocMem(1000, MEMF_CHIP|MEMF_CLEAR);
UWORD *copperlist_ptr = (UWORD *)copperlist;
 
// add a bitplane
*(copperlist_ptr)++ = BPL1PTH;
*(copperlist_ptr)++ = ((ULONG)bitplane >> 16) & 0xffff;
*(copperlist_ptr)++ = BPL1PTL;
*(copperlist_ptr)++ = (ULONG)bitplane & 0xffff;
 
// add a color
*(copperlist_ptr)++ = COLOR00;
*(copperlist_ptr)++ = 0x000;
 
// add a WAIT
*(copperlist_ptr)++ = ((currentY & 0xff) << 8) | 0x26 | 0x1;
*(copperlist_ptr)++ = 0xfffe;
 
// do a PAL workaround
if (currentY == 255) {
  *(copperlist_ptr++) = 0xffdf;
  *(copperlist_ptr++) = 0xfffe;
}
 
// add an END
*(copperlist_ptr)++ = 0xffff;
*(copperlist_ptr)++ = 0xfffe;

PAL copperlist workaround

snippet.asm
  MOVEQ #0,D0 ; current raster line
  LEA Copperlist,A5
LoopToRenderLines:
  MOVE.L D0,D1
  AND.L #$FF,D1
  ROL.W #8,D1
  ADDQ #1,D1
  MOVE.W D1,(A5)+
  MOVE.W $FF00,(A5)+ ; obey only vertical line
 
  ADDQ #1,D0
 
  CMP #256,D0
  ; this has to happen right before you write copperlist entries to line 256 or above
  BNE _NoPALFix
  ; these come straight from http://vikke.net/index.php?id=copperbars-1
  MOVE.W #$FFDF, (A5)+ ; %1111111111011111, wait for Y=255, X=111*4=444
  MOVE.W #$FFFE, (A5)+ ; %1111111111111110 = blitter-finished-disable =1, all compare bits enabled
NoPALFix:
  CMP #312,D0
  BNE LoopToRenderLines

Code Issues

  • Writing a copperlist and the display becomes corrupted the longer the list gets?
    • Ensure you have enough space for the copperlist. You're likely writing into whatever memory was reserved for a bitplane.