#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "system/blitter.h" #include "system/copper.h" #include "system/system.h" #include "system/sprite.h" #include "screen.h" #include "types.h" #include "bun.h" /** * This barely gets 50fps on an 020 but I'm leaving it for now. * The demo is definitely CPU bound. * * Potential improvements: * * [ ] Precalculate even more of bun.c. This may get 50fps on an A500 if I do this. * It would require more significant caching of as much stuff as possible. * The A500 is spending entirely too much time on CPU tasks. * [ ] Topaz re-rendering is limited to squares or rows that changed in the last frame. * I tried this once but I need a more precise way of redrawing those areas. * [ ] Cool bun clears and re-renders happen in the same pass. * This would likely mean extending the cool bun art to have a blank word on the left, * and enough blank rows above and below to cover clearing areas above and below. */ extern struct Custom far custom; extern struct CIA far ciaa; // change to 0 to not render sprites #define RENDER_SPRITES (1) struct ScreenDefinition screenDefinition; struct ActiveScreenBufferDetails activeScreenBufferDetails; /** * The locations within the copperlist data where the bitplanes are referenced. * We change these areas of memory directly to implement double buffering. * Addresses are to the high, then low, word of the plane memory address in the copperlist. * * @see addDisplayToCopperlist */ void *copperlistBitplanePointers[8][2]; uint16_t custom_color = (uint16_t)offsetof(Custom, color); uint16_t custom_sprite = (uint16_t)offsetof(Custom, sprpt); uint16_t custom_sprite_control = (uint16_t)offsetof(Custom, spr); extern uint8_t chip TopazBitplanes[]; extern uint16_t chip CopperColors[]; extern uint16_t chip SpriteCopperlist[]; extern uint16_t chip SpriteData[]; extern uint8_t chip MaskBitplane[]; #define TOPAZ_WIDTH_PIXELS (160) #define TOPAZ_WIDTH_BYTES (TOPAZ_WIDTH_PIXELS / 8) #define TOPAZ_WIDTH_WORDS (TOPAZ_WIDTH_PIXELS / 16) void renderTopaz(void) { int plane; uint16_t bltcmod; uint8_t *bltbpt; bltcmod = screenDefinition.byteWidth - TOPAZ_WIDTH_BYTES; bltbpt = TopazBitplanes; for (plane = 0; plane < 3; ++plane) { custom.bltcon0 = BLTCON0(0xca, 1, 1, 1, 1, 0); custom.bltcon1 = 0; custom.bltafwm = 0xffff; custom.bltalwm = 0xffff; custom.bltapt = MaskBitplane; custom.bltbpt = bltbpt; custom.bltcpt = activeScreenBufferDetails.planes[plane] + 8; custom.bltdpt = activeScreenBufferDetails.planes[plane] + 8; custom.bltamod = 0; custom.bltbmod = 0; custom.bltcmod = bltcmod; custom.bltdmod = bltcmod; custom.bltsize = BLTSIZE(TOPAZ_WIDTH_WORDS, 256); bltbpt += TOPAZ_WIDTH_BYTES * 256; WaitBlit(); } } #define MOSTLY_TOPAZ_TOP_BOTTOM_CROP (30) void renderMostlyTopaz(void) { int plane; uint16_t bltcmod; uint8_t *bltbpt; bltcmod = screenDefinition.byteWidth - TOPAZ_WIDTH_BYTES; bltbpt = TopazBitplanes; for (plane = 0; plane < 2; ++plane) { custom.bltcon0 = BLTCON0(0xca, 1, 1, 1, 1, 0); custom.bltcon1 = 0; custom.bltafwm = 0xffff; custom.bltalwm = 0xffff; custom.bltapt = MaskBitplane + (MOSTLY_TOPAZ_TOP_BOTTOM_CROP * TOPAZ_WIDTH_BYTES); custom.bltbpt = bltbpt + (MOSTLY_TOPAZ_TOP_BOTTOM_CROP * TOPAZ_WIDTH_BYTES); custom.bltcpt = MOSTLY_TOPAZ_TOP_BOTTOM_CROP * screenDefinition.byteWidth + activeScreenBufferDetails.planes[plane] + 8; custom.bltdpt = MOSTLY_TOPAZ_TOP_BOTTOM_CROP * screenDefinition.byteWidth + activeScreenBufferDetails.planes[plane] + 8; custom.bltamod = 0; custom.bltbmod = 0; custom.bltcmod = bltcmod; custom.bltdmod = bltcmod; custom.bltsize = BLTSIZE(TOPAZ_WIDTH_WORDS, 256 - MOSTLY_TOPAZ_TOP_BOTTOM_CROP * 2); bltbpt += TOPAZ_WIDTH_BYTES * 256; WaitBlit(); } } #define COPPERLIST_SIZE (10000) uint16_t *copperlist; void buildCopperlist(void) { uint32_t spriteDataPointer; uint16_t *currentCopperlist, *currentCopperColors, *currentSpriteCopperlist; int i, j, y; uint16_t spriteSetupRegisters[3]; copperlist = prepareNewCopperlist(COPPERLIST_SIZE); currentCopperlist = addDisplayToCopperlist( copperlist, &screenDefinition, &activeScreenBufferDetails, &copperlistBitplanePointers ); currentCopperColors = CopperColors; currentSpriteCopperlist = SpriteCopperlist; COPPERLIST_MOVE(currentCopperlist, custom_color, 0x3a6); COPPERLIST_MOVE(currentCopperlist, custom_color + 2, 0x000); COPPERLIST_MOVE(currentCopperlist, custom_color + 4, 0xfff); if (RENDER_SPRITES) { for (i = 0; i < 8; ++i) { spriteDataPointer = (uint32_t)&SpriteData; spriteDataPointer += ((256 + 2) * 4) * i; COPPERLIST_MOVE_POINTER( currentCopperlist, custom_sprite + i * 4, spriteDataPointer ); } } else { currentCopperlist = setUpEmptySpritesInCopperlist(currentCopperlist); } for (y = 0; y < 256; ++y) { if (RENDER_SPRITES) { for (i = 0; i < 8; ++i) { spriteSetupRegisters[0] = custom_color + 32 + ( // sprite color group (i / 2) * 4 + // 0 is transparent 1 + i % 2 ) * 2; spriteSetupRegisters[1] = custom_sprite_control + i * 8; spriteSetupRegisters[2] = custom_sprite_control + i * 8 + 2; for (j = 0; j < 3; ++j) { COPPERLIST_MOVE( currentCopperlist, spriteSetupRegisters[j], *(currentSpriteCopperlist++) ); } } } else { //printf("Skipping render\n"); } for (i = 3; i < 8; ++i) { COPPERLIST_MOVE( currentCopperlist, custom_color + (i * 2), *(currentCopperColors++) ); } COPPERLIST_WAIT( currentCopperlist, (31 + (256 / 4)), (44 + y), 0xFFFE ); } endCopperlist(currentCopperlist); } static struct MsgPort *keyboardMessagePort = NULL; static struct IOStdReq *keyboardIO = NULL; static struct Interrupt keyboardInterrupt; extern far ULONG keyboardPressed; extern struct InputEvent * __asm KeyboardHandler( register __a0 struct InputEvent *input, register __a1 APTR id ); void setupKeyboard(void) { if (keyboardMessagePort = CreatePort(NULL, NULL)) { if (keyboardIO = (struct IOStdReq *)CreateExtIO( keyboardMessagePort, sizeof(struct IOStdReq) )) { // OpenDevice returns 0 if successful if (!OpenDevice( "input.device", 0, (struct IORequest *)keyboardIO, 0 )) { keyboardInterrupt.is_Node.ln_Type = NT_INTERRUPT; keyboardInterrupt.is_Node.ln_Pri = 100; keyboardInterrupt.is_Node.ln_Name = (STRPTR)"cool bun"; keyboardInterrupt.is_Code = (void (*)())&KeyboardHandler; keyboardIO->io_Data = (void *)&keyboardInterrupt; keyboardIO->io_Command = IND_ADDHANDLER; DoIO((struct IORequest *)keyboardIO); } } } } void teardownKeyboard(void) { if (keyboardIO) { keyboardIO->io_Data = (void *)&keyboardInterrupt; keyboardIO->io_Command = IND_REMHANDLER; DoIO((struct IORequest *)keyboardIO); CloseDevice((struct IORequest *)keyboardIO); DeleteIORequest((struct IORequest *)keyboardIO); keyboardIO = NULL; } if (keyboardMessagePort) { DeleteMsgPort(keyboardMessagePort); keyboardMessagePort = NULL; } } int main(void) { int i; struct BunRenderer bunRenderer; uint16_t redrawRanges[BUN_COUNT][4]; printf("\nCool bun blitter, copper, and sprite demo by John Bintz\n"); printf("\n"); printf("This is my first real go at writing graphics code for the Amiga.\n"); printf("It's a combination of C (SAS/C) and Assembler (DevPac), with a Ruby\n"); printf("script to do some image processing. The screen only uses three\n"); printf("bitplanes -- one color is the background, two colors are the flying\n"); printf("cool bun logo white and black, and the remaining 5 colors are used\n"); printf("by the Topaz smiling art. The Ruby script will process the PNG file\n"); printf("and figure out the best 5 colors (plus b&w) for that line of the\n"); printf("image, setting the colors via copperlist for that line.\n"); printf("It also builds the clipping mask for the blitter.\n\n"); printf("And, I'm also using sprites to add more colors to the Topaz art.\n"); printf("The Ruby script will figure out what small areas of each line weren't\n"); printf("covered by the bitplane colors and use the 8 available sprites,\n"); printf("changing the colors on each line via copper, to fill in the gaps.\n"); printf("Cool buns are animated via blitter rather than sprites, but I\n"); printf("managed to get it to run at a decent speed regardless.\n"); printf("\nEnjoy, and thanks for watching!\n"); printf("John (theindustriousrabbit.com)\n\n"); allocateDoubleBufferedScreenMemory( &screenDefinition, &activeScreenBufferDetails, SCREEN_WIDTH, SCREEN_HEIGHT, 3 ); setupBunRenderer( &bunRenderer, &screenDefinition, &activeScreenBufferDetails ); buildCopperlist(); /* copperlist debugging for (i = 0; i < 20; ++i) { printf( "%x %x %x %x\n", *(copperlist + (i * 4)), *(copperlist + (i * 4) + 1), *(copperlist + (i * 4) + 2), *(copperlist + (i * 4) + 3) ); } */ setupKeyboard(); takeOverSystem(); setCopperlist(copperlist); setUpDisplay((uint32_t)screenDefinition.bitplanes); renderTopaz(); swapCurrentScreenBuffer(&screenDefinition, &activeScreenBufferDetails); renderTopaz(); swapCurrentScreenBuffer(&screenDefinition, &activeScreenBufferDetails); updateDisplayInCopperList( &screenDefinition, &activeScreenBufferDetails, copperlistBitplanePointers ); i = 0; while (1) { WaitTOF(); swapCurrentScreenBuffer(&screenDefinition, &activeScreenBufferDetails); clearCurrentBuns(&bunRenderer); renderBunFrame(i, &bunRenderer); renderMostlyTopaz(); updateDisplayInCopperList( &screenDefinition, &activeScreenBufferDetails, copperlistBitplanePointers ); if (keyboardPressed) break; if ((ciaa.ciapra >> 6) != 3) break; i++; if (i > FRAMES_FOR_SCREEN) i = 0; } // somthing in here causes an A500 in KS 1.3 to crash giveBackSystem(); teardownKeyboard(); teardownScreen(&screenDefinition); freeCopperlist(copperlist); teardownBunRenderer(); return 0; }