Amiga Agnus Blitter

The Amiga's graphics multitool. Lets you do:

  • copies from up to three areas of Chip RAM with logical operations
  • line drawing
  • horizontal fills

About Horizontal Fills

The fill operation has to be done as part of a copy. If you want to draw a polygon and then fill it, the order of operations is:

  • Draw all the lines
  • Blitter copy the area with all the lines to a scratch area, enabling fill
  • Blitter copy the scratch area elsewhere

Gotchas

  • Before you do blitter stuff, swap out your own dummy copper list, otherwise an existing copper list can blow away what you're trying to do.
  • Wait for one blitter operation to finish before starting another. You have these options:
    • Use graphics/WaitBlit
    • Wait for bit 14 (DMAB_BLTDONE) to become 0. The Amiga Hardware Manual gives the first half of this code. Here's your own basic blitter wair.
snippet.asm
_MyWaitBlit:
  LEA _custom,A0
 
  BTST.B #DMAB_BLTDONE-8,dmaconr(A0)
  BTST.B #DMAB_BLTDONE-8,dmaconr(A0)
  BEQ .done
 
.loop:
  BTST.B #DMAB_BLTDONE-8,dmaconr(A0)
  BNE .loop
 
.done:
  RTS
  • Blit wait (cont'd)
    • Use Interrupts. I haven't done this yet.

How-Tos

Set up BLTSIZE programmatically

snippet.asm
  MOVEQ #0,D0
  MOVEQ #0,D1
  MOVE.W #320,D0 ; width
  ASR.W #4,D0 ; in words
  MOVE.W #256,D1 ; height
  ASL.W #6,D1 ; shifted 6 bits
  ADD.W D1,D0
  MOVE.W D0,BLTSIZE ; start blit

Draw a line across multiple bitplanes

This draws heavily from:

snippet.c
struct BlitDrawLineSetup {
  int dx, dy;
  int sud, sul, aul, accumulator, sign;
  int bltapt;
  int bltamod;
  int bltbmod;
  unsigned char *target;
};
 
void BlitDrawLine(
  struct ScreenSetup *screenSetup,
  struct BlitDrawLineSetup *setup,
  uint16_t sx,
  uint16_t sy,
  uint16_t ex,
  uint16_t ey,
  uint8_t color
) {
  unsigned char *target = screenSetup->memoryStart + (((screenSetup->width * sy + sx) >> 3) & 0xfffe);
  int i;
 
  setup->dx = ex - sx;
  setup->dy = ey - sy;
 
  if (setup->dx == 0 && setup->dy == 0) return;
 
  setup->sud = abs(setup->dy) < abs(setup->dx);
  if (!setup->sud) { i = setup->dx; setup->dx = setup->dy; setup->dy = i; }
 
  setup->sul = setup->dy < 0;
  setup->dy = abs(setup->dy);
 
  setup->aul = setup->dx < 0;
  setup->dx = abs(setup->dx);
 
  setup->accumulator = 4 * setup->dy - 2 * setup->dx;
  setup->sign = setup->accumulator < 0;
 
  for (i = 0; i < screenSetup->bitplanes; ++i) {
    custom.bltadat = 0x8000;
    custom.bltbdat = color & 1 == 1 ? 0xffff : 0;
 
    custom.bltafwm = 0xffff;
    custom.bltalwm = 0xffff;
 
    custom.bltamod = 4 * (setup->dy - setup->dx);
    custom.bltbmod = 4 * setup->dy;
    custom.bltcmod = screenSetup->width / 8;
    custom.bltdmod = screenSetup->width / 8;
 
    custom.bltapt = setup->accumulator;
    custom.bltcpt = target + screenSetup->nextBitplaneAdvance * i;
    custom.bltdpt = target + screenSetup->nextBitplaneAdvance * i;
 
    custom.bltcon0 = ((sx % 16) << 12) + (11 << 8) + 0xca;
    custom.bltcon1 = 1 + (setup->aul << 2) + (setup->sul << 3) + (setup->sud << 4) + (setup->sign << 6);
 
    custom.bltsize = 0x02 + ((setup->dx + 1) << 6);
    WaitBlit();
 
    color >>= 1;
  }
}