amiga-agnus-copperbars/actual complex demo.c

432 lines
11 KiB
C

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <devices/timer.h>
#include <clib/timer_protos.h>
#include <graphics/GfxBase.h>
#include <hardware/cia.h>
#include <hardware/dmabits.h>
#include <stdio.h>
/**
* Complex copper demo
* * 20 colors on a 4 color display
* * Single buffered bitplane updating
*
* Leave the slow speed on A500 for another video to solve
*/
extern struct GfxBase *GfxBase;
extern struct Custom far custom;
extern struct CIA far ciaa, ciab;
// this is faster than
// custom.vhposr + (custom.vposr << 16)
static volatile ULONG *custom_vposr = (volatile ULONG *)0xdff004;
struct Library *TimerBase;
#define offsetof(s, m) &((struct s *)0)->m
#define BPLCON0_COLOR (0x200)
#define BPLCON0_BPU_2 (0x2000)
#define COPPERLIST_SIZE (20000)
#define DISPLAY_TOP (44)
#define SAWTOOTH_STEPS (8)
#define TOTAL_SAWTOOTH_STEPS (SAWTOOTH_STEPS * 2 - 2)
#define BITPLANE_SIZE (320/8)*(256+TOTAL_SAWTOOTH_STEPS)
#define BAR_HEIGHT (20)
#define COP_WAIT(x,y) ((y & 0xff) << 8) | x | 0x01
#define COP_WAITMASK (0xfffe)
#define CPTR *(copperlist_ptr++)
#define COLOR00 0x180
#define COLOR01 0x182
#define COLOR02 0x184
#define COLOR03 0x186
void buildSawtoothSlice(ULONG *dest, int i) {
ULONG base;
// left side bpl 1
base = 0x1fffffff;
base = base >> i;
dest[0] = base;
// right side bpl 1
base = 0xfffffff0;
base = base << (8 - i);
dest[1] = base;
// left side bpl 2
base = 0x0fffffff;
base = base >> i;
dest[2] = base;
// right side bpl 2
base = 0xfffffff8;
base = base << (8 - i);
dest[3] = base;
}
long getRandomSeed(void) {
long randomSeed = 0;
struct MsgPort *TimerMP;
struct timerequest *TimerIO;
struct timeval currentSystemTime;
// get a random seed by using the amount of microseconds the machine's
// been on, modulo 1000000.
//
// if for some weird reaon timer.device is not available,
// we can continue.
if (TimerMP = CreatePort(NULL, NULL)) {
if (TimerIO = (struct timerequest *)CreateExtIO(TimerMP, sizeof(struct timerequest))) {
if (!(OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)TimerIO, 0))) {
TimerBase = (struct Library *)TimerIO->tr_node.io_Device;
GetSysTime(&currentSystemTime);
randomSeed = currentSystemTime.tv_micro;
CloseDevice((struct IORequest *)TimerIO);
}
DeleteExtIO((struct IORequest *)TimerIO);
}
DeletePort(TimerMP);
}
return randomSeed;
}
void buildSawtooth(ULONG verticalBarSlices[]) {
int i, j;
// create the sawtooth pattern
for (i = 0; i < SAWTOOTH_STEPS; ++i) {
buildSawtoothSlice(&verticalBarSlices[i * 4], i);
}
for (i = 0; i < SAWTOOTH_STEPS - 2; ++i) {
j = SAWTOOTH_STEPS - 2 - i;
buildSawtoothSlice(&verticalBarSlices[(SAWTOOTH_STEPS * 4) + (i * 4)], j);
}
}
#define BITPLANE_ADVANCE ((320/32) - 6)
#define BITPLANE_WORD_SIZE ((320/32)*(256+TOTAL_SAWTOOTH_STEPS))
/**
* Write sawtooth pattern to provided bitplanes.
*/
void writeSawtoothToBitplanes(APTR *bitplanes, ULONG verticalBarSlices[]) {
ULONG *bitplane_1 = (ULONG *)bitplanes + 1;
ULONG *bitplane_2 = (ULONG *)bitplanes + BITPLANE_WORD_SIZE + 1;
int currentSlice = 0, currentY, sliceOffset;
for (currentY = 0; currentY < (255 + TOTAL_SAWTOOTH_STEPS); ++currentY) {
sliceOffset = currentSlice * 4;
// left
*(bitplane_1++) = verticalBarSlices[sliceOffset++];
*(bitplane_1) = verticalBarSlices[sliceOffset++];
*(bitplane_2++) = verticalBarSlices[sliceOffset++];
*(bitplane_2) = verticalBarSlices[sliceOffset++];
bitplane_1 += 4;
bitplane_2 += 4;
sliceOffset = currentSlice * 4;
// right
*(bitplane_1++) = verticalBarSlices[sliceOffset++];
*(bitplane_1) = verticalBarSlices[sliceOffset++];
*(bitplane_2++) = verticalBarSlices[sliceOffset++];
*(bitplane_2) = verticalBarSlices[sliceOffset++];
bitplane_1 += BITPLANE_ADVANCE;
bitplane_2 += BITPLANE_ADVANCE;
currentSlice += 1;
if (currentSlice == TOTAL_SAWTOOTH_STEPS) currentSlice = 0;
}
}
int BAR_COLORS[2][20] = {
{
0x300,
0x500, 0x500,
0x700, 0x700,
0xa00, 0xa00, 0xa00,
0xf00, 0xf00, 0xfdd, 0xf00,
0xa00, 0xa00, 0xa00,
0x700, 0x700,
0x500, 0x500,
0x300
}, {
0x003,
0x005, 0x005,
0x007, 0x007,
0x00a, 0x00a, 0x00a,
0x00f, 0x00f, 0xddf, 0x00f,
0x00a, 0x00a, 0x00a,
0x007, 0x007,
0x005, 0x005,
0x003
}
};
int main(void) {
struct View *OldView = ((struct GfxBase *)GfxBase)->ActiView;
ULONG OldCopper = (ULONG)((struct GfxBase *)GfxBase)->copinit;
ULONG OldDMACON, OldINTENA, OldINTREQ, OldADKCON;
int currentY, frame = 0;
int currentHighlight = 0, currentBar;
int barPosition[2] = {150, 250};
int barBottomPosition[2] = { 150 + BAR_HEIGHT, 250 + BAR_HEIGHT };
int barDestination[2] = {230, 40};
int barSpeed[2] = {1, -2};
int firstInFront = 0, actualBar;
int barsDrawn[2];
UWORD barColor, lastBarColor, lastHighlightColor, highlightColor, shadowColor;
long randomSeed;
// 2 per slice * 8 different slices * 2 bitplanes * replicate for sawtooth
ULONG verticalBarSlices[TOTAL_SAWTOOTH_STEPS * 4 * 2];
void *copperlist;
UWORD *copperlist_ptr;
APTR *bitplanes;
// how much can we avoid hard coding into our code?
UWORD custom_bplpt = (UWORD)offsetof(Custom, bplpt);
//UWORD custom_color = (UWORD)offsetof(Custom, color);
randomSeed = getRandomSeed();
buildSawtooth(verticalBarSlices);
LoadView(NULL);
WaitTOF();
WaitTOF();
OwnBlitter();
WaitBlit();
Forbid();
OldDMACON = custom.dmaconr | 0x8000;
OldINTENA = custom.intenar | 0x8000;
OldINTREQ = custom.intreqr | 0x8000;
OldADKCON = custom.adkconr | 0x8000;
// disable interrupts
custom.intena = 0xc000;
custom.intena = 0x3fff;
// set up dma
// weirdly i need DMAF_AUDIO enabled in order for FastRand() to work...
custom.dmacon = DMAF_SETCLR | DMAF_COPPER | DMAF_RASTER | DMAF_AUDIO | DMAF_DISK;
custom.dmacon = DMAF_SPRITE | DMAF_BLITTER;
// TODO: determine optimal size
copperlist = AllocMem(COPPERLIST_SIZE, MEMF_CHIP | MEMF_CLEAR);
// bitplanes are always same size regardless of anim size
bitplanes = (APTR *)AllocMem(BITPLANE_SIZE * 2, MEMF_PUBLIC|MEMF_CHIP|MEMF_CLEAR);
// default copperlist has just STOP
((ULONG *)copperlist)[0] = 0xfffffffe;
// set up 2 bitplane pal display
custom.bplcon0 = BPLCON0_COLOR | BPLCON0_BPU_2;
custom.bplcon1 = 0;
custom.bpl1mod = 0;
custom.diwstrt = 0x2c21;
custom.diwstop = 0x2cc1;
custom.ddfstrt = 0x0038;
custom.ddfstop = 0x00d0;
// substitute our minimal copperlist now
custom.cop1lc = (ULONG)copperlist;
writeSawtoothToBitplanes(
bitplanes,
verticalBarSlices
);
lastBarColor = 0xffff;
lastHighlightColor = 0xffff;
// neither HID button 0 is down
while ((ciaa.ciapra >> 6) == 3) {
// rebuild copperlist
copperlist_ptr = (UWORD *)copperlist;
currentHighlight = ((frame + TOTAL_SAWTOOTH_STEPS) % TOTAL_SAWTOOTH_STEPS);
actualBar = firstInFront;
barsDrawn[0] = 0;
barsDrawn[1] = 0;
// bpl1pth
*(copperlist_ptr++) = custom_bplpt;
*(copperlist_ptr++) = (((ULONG)bitplanes + (frame * (320/8)) >> 16)) & 0xffff;
// bpl1ptl
*(copperlist_ptr++) = custom_bplpt + 2;
*(copperlist_ptr++) = ((ULONG)bitplanes + (frame * (320/8))) & 0xffff;
// bpl2pth
*(copperlist_ptr++) = custom_bplpt + 4;
*(copperlist_ptr++) = ((((ULONG)bitplanes) + BITPLANE_SIZE + (frame * (320/8))) >> 16) & 0xffff;
// bpl2ptl
*(copperlist_ptr++) = custom_bplpt + 6;
*(copperlist_ptr++) = (((ULONG)bitplanes) + BITPLANE_SIZE + (frame * (320/8))) & 0xffff;
*(copperlist_ptr++) = COLOR00;
*(copperlist_ptr++) = 0x0000;
for (currentY = DISPLAY_TOP; currentY < DISPLAY_TOP + 256; currentY++) {
// 8 of highlight on color02, then 6 of shadow on color03
// 8 of lesser shadow on color01, then 6 of lesser highlight on color02
// color01 for now stays the same
CPTR = COP_WAIT(0x10, currentY);
CPTR = COP_WAITMASK;
CPTR = COLOR03;
CPTR = 0x779;
if (currentHighlight < SAWTOOTH_STEPS) {
highlightColor = 0xfff;
shadowColor = 0x777;
} else {
highlightColor = 0x444;
shadowColor = 0xaaa;
}
// only write shading to copperlist if it changes
if (highlightColor != lastHighlightColor) {
CPTR = COLOR02;
CPTR = highlightColor;
CPTR = COLOR01;
CPTR = shadowColor;
lastHighlightColor = highlightColor;
}
if (!barsDrawn[0] || !barsDrawn[1]) {
barColor = 0x000;
for (currentBar = 0; currentBar < 2; currentBar++) {
if (!barsDrawn[actualBar]) {
if (currentY >= barPosition[actualBar]) {
if (currentY < barBottomPosition[actualBar]) {
barColor = BAR_COLORS[actualBar][currentY - barPosition[actualBar]];
} else {
barsDrawn[actualBar] = 1;
}
}
}
actualBar = 1 - actualBar;
}
if (barColor != lastBarColor) {
CPTR = COLOR00;
CPTR = barColor;
lastBarColor = barColor;
}
}
CPTR = COP_WAIT(0x70, currentY);
CPTR = COP_WAITMASK;
CPTR = COLOR03;
CPTR = 0x977;
// PAL workaround
if (currentY == 255) {
*(copperlist_ptr++) = 0xffdf;
*(copperlist_ptr++) = 0xfffe;
}
currentHighlight += 1;
if (currentHighlight == TOTAL_SAWTOOTH_STEPS) currentHighlight = 0;
}
// reset color 0
CPTR = COP_WAIT(0x10, DISPLAY_TOP + 256);
CPTR = COP_WAITMASK;
CPTR = COLOR00;
CPTR = 0x0000;
// end the copperlist
*(copperlist_ptr++) = 0xffff;
*(copperlist_ptr++) = 0xfffe;
// vbl wait
// this will not work on NTSC!
while ((*custom_vposr & 0x1FF00) != ((256 + DISPLAY_TOP) << 8)) {}
for (currentBar = 0; currentBar < 2; currentBar++) {
barPosition[currentBar] += barSpeed[currentBar];
barBottomPosition[currentBar] += barSpeed[currentBar];
if (barSpeed[currentBar] > 0) {
if (barPosition[currentBar] > barDestination[currentBar]) {
barSpeed[currentBar] = -(FastRand(randomSeed) % 3 + 2);
barDestination[currentBar] = FastRand(randomSeed) % 128 + DISPLAY_TOP;
}
} else {
if (barPosition[currentBar] < barDestination[currentBar]) {
barSpeed[currentBar] = FastRand(randomSeed) % 3 + 2;
barDestination[currentBar] = FastRand(randomSeed) % 128 + 128 + DISPLAY_TOP - BAR_HEIGHT;
firstInFront = 1 - firstInFront;
}
}
}
randomSeed += frame;
if (randomSeed > 0xffffffff) randomSeed -= 0xffffffff;
frame += 1;
if (frame == TOTAL_SAWTOOTH_STEPS) frame = 0;
}
FreeMem(bitplanes, BITPLANE_SIZE);
FreeMem(copperlist, COPPERLIST_SIZE);
custom.dmacon = 0x7FFF;
custom.dmacon = OldDMACON;
custom.intena = 0x7FFF;
custom.intena = OldINTENA;
custom.intreq = 0x7FFF;
custom.intreq = OldINTREQ;
custom.adkcon = 0x7FFF;
custom.adkcon = OldADKCON;
custom.cop1lc = OldCopper;
LoadView(OldView);
WaitTOF();
WaitTOF();
WaitBlit();
DisownBlitter();
Permit();
}