# 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. * http://eab.abime.net/showthread.php?t=99892 * Symptom: A large blitter copy works up to a certain scanline height, and trying to do any blitter stuff with a larger size fails. * 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. ```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. ## Links * https://www.markwrobel.dk/post/amiga-machine-code-letter6-blitter2/ * https://www.markwrobel.dk/post/amiga-machine-code-letter6-blitter3/ * https://www.markwrobel.dk/post/amiga-machine-code-letter7/ * http://coppershade.org/articles/AMIGA/Agnus/Programming_the_Blitter/ ## How-Tos ### Set up BLTSIZE programmatically ```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: * https://www.markwrobel.dk/post/amiga-machine-code-letter12-linedraw2/ * http://amigadev.elowar.com/read/ADCD_2.1/Hardware_Manual_guide/node0128.html ```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; } } ```