Initial commit

main
John Bintz 2023-06-20 07:15:36 -04:00
commit 6d58dd3fe9
10 changed files with 1022 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.uaem
*.o
*.lnk
*.info

37
README.md Normal file
View File

@ -0,0 +1,37 @@
# Amiga Copperbars Examples from "Amiga rasterbar are cool"
These are the code files used to generate the rasterbars effects on the
Agnus/Copper Industrious Rabbit video. I built them to compile in SAS/C
6.58:
`sc <filename> link`
They're written in C and, except for `actual complex demo.c`, are left
pretty unoptimized so they're more readable. Writing them in assembler
and with more tricks can get them to perform faster, at the expense of
a lot of readability for a beginner.
`actual complex demo.c` will not run at full 50FPS on a stock Amiga 500, but
`intro.c` will (or at least should).
They also don't work on NTSC Amigas! I wait for a position well below
the max NTSC Copper vertical position to detect the end of the vertical
blank. If you want to modify these for NTSC, please send patches
to me via the contact instructions on the
[About page](https://theindustriousrabbit.com/about).
## Intro
The three horizontal bars moving up and down at the beginning of the video.
## Chunky Vertical, Two Columns of Lines, and Actual Complex Demo
The three different demos at the end of the video.
## Random numbers
Intro and Actual Complex Demo both use [`FastRand`](http://amiga.nvg.org/amiga/reference/Includes_and_Autodocs_2._guide/node015A.html)
to generate pseudo-random results in the demo, using the microseconds since
the machine was turned on as the initial seed
(using [timer.device](http://www.amigadev.elowar.com/read/ADCD_2.1/Devices_Manual_guide/node00C1.html)).
This required enabling audio DMA for some reason that I don't understand.

BIN
actual complex demo Normal file

Binary file not shown.

431
actual complex demo.c Normal file
View File

@ -0,0 +1,431 @@
#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();
}

BIN
chunky vertical Normal file

Binary file not shown.

160
chunky vertical.c Normal file
View File

@ -0,0 +1,160 @@
/**
* Chunky vertical copperbars demo
* Copyright 2023 John Bintz
* Licensed under the MIT License
* https://theindustriousrabbit.com
*/
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <graphics/GfxBase.h>
#include <exec/memory.h>
#include <hardware/cia.h>
#include <hardware/dmabits.h>
#include <stdio.h>
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;
#define offsetof(s, m) &((struct s *)0)->m
#define BITPLANE_SIZE (320/8)*256
#define BPLCON0_COLOR (0x200)
#define BPLCON0_BPU_1 (0x1000)
static UWORD colors[] = {0xf00,0x0f0,0xff0,0x0ff};
int main(void) {
struct View *OldView = ((struct GfxBase *)GfxBase)->ActiView;
ULONG OldCopper = (ULONG)((struct GfxBase *)GfxBase)->copinit;
ULONG OldDMACON, OldINTENA, OldINTREQ, OldADKCON;
int currentY, colorBar, xPos;
void *copperlist;
UWORD *copperlist_ptr;
APTR *bitplane;
// how much can we avoid hard coding into our code?
UWORD custom_bplpt = (UWORD)offsetof(Custom, bplpt);
UWORD custom_color = (UWORD)offsetof(Custom, color);
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
custom.dmacon = DMAF_SETCLR | DMAF_COPPER | DMAF_RASTER;
custom.dmacon = DMAF_AUDIO | DMAF_DISK | DMAF_SPRITE | DMAF_BLITTER;
// 4 bars = WAIT + MOVE (8 bytes) 8 times * 312 + ~100 for setup = 21000 should do it
copperlist = AllocMem(21000, MEMF_CHIP | MEMF_CLEAR);
bitplane = (APTR *)AllocMem(BITPLANE_SIZE, MEMF_PUBLIC|MEMF_CHIP|MEMF_CLEAR);
// default copperlist has just STOP
((ULONG *)copperlist)[0] = 0xfffffffe;
// set up 1 bitplane pal display
custom.bplcon0 = BPLCON0_COLOR | BPLCON0_BPU_1;
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;
// neither HID button 0 is down
while ((ciaa.ciapra >> 6) == 3) {
// rebuild copperlist
copperlist_ptr = (UWORD *)copperlist;
// bpl1pth
*(copperlist_ptr++) = custom_bplpt;
*(copperlist_ptr++) = ((ULONG)bitplane >> 16) & 0xffff;
// bpl1ptl
*(copperlist_ptr++) = custom_bplpt + 2;
*(copperlist_ptr++) = (ULONG)bitplane & 0xffff;
*(copperlist_ptr++) = custom_color;
*(copperlist_ptr++) = 0x0f00;
for (currentY = 0; currentY < 312; currentY++) {
for (colorBar = 0; colorBar < 4; colorBar++) {
xPos = 0x68 + (colorBar * 24);
*(copperlist_ptr++) = ((currentY & 0xff) << 8) | xPos | 0x1;
*(copperlist_ptr++) = 0xfffe;
*(copperlist_ptr++) = custom_color;
*(copperlist_ptr++) = colors[colorBar];
*(copperlist_ptr++) = ((currentY & 0xff) << 8) | xPos + 2 | 0x1;
*(copperlist_ptr++) = 0xfffe;
*(copperlist_ptr++) = custom_color;
*(copperlist_ptr++) = 0x000;
}
// PAL workaround
if (currentY == 255) {
*(copperlist_ptr++) = 0xffdf;
*(copperlist_ptr++) = 0xfffe;
}
}
// end the copperlist
*(copperlist_ptr++) = 0xffff;
*(copperlist_ptr++) = 0xfffe;
// vbl wait
// this will not work on NTSC!
while ((*custom_vposr & 0x1FF00) != (300 << 8)) {}
}
FreeMem(bitplane, BITPLANE_SIZE);
FreeMem(copperlist, 21000);
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();
}

BIN
intro Normal file

Binary file not shown.

231
intro.c Normal file
View File

@ -0,0 +1,231 @@
/**
* Intro copperbars demo
* Copyright 2023 John Bintz
* Licensed under the MIT License
* https://theindustriousrabbit.com
*/
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/timer_protos.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <devices/timer.h>
#include <graphics/GfxBase.h>
#include <hardware/cia.h>
#include <hardware/dmabits.h>
#include <stdio.h>
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;
struct MsgPort *TimerMP;
struct timerequest *TimerIO;
#define offsetof(s, m) &((struct s *)0)->m
#define BITPLANE_SIZE (320/8)*256
#define BAR_HEIGHT (50)
#define BPLCON0_COLOR (0x200)
#define BPLCON0_BPU_1 (0x1000)
int main(void) {
struct View *OldView = ((struct GfxBase *)GfxBase)->ActiView;
ULONG OldCopper = (ULONG)((struct GfxBase *)GfxBase)->copinit;
ULONG OldDMACON, OldINTENA, OldINTREQ, OldADKCON;
unsigned short redY = 0, greenY = 10, blueY = 20;
unsigned short redSpeed = 1, greenSpeed = 2, blueSpeed = 3;
unsigned short currentY = 0, currentColor, newColor;
long randomSeed = 0;
struct timeval currentSystemTime;
void *copperlist;
UWORD *copperlist_ptr;
APTR *bitplane;
// how much can we avoid hard coding into our code?
UWORD custom_bplpt = (UWORD)offsetof(Custom, bplpt);
UWORD custom_color = (UWORD)offsetof(Custom, color);
// 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);
}
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;
custom.dmacon = DMAF_DISK | DMAF_SPRITE | DMAF_BLITTER;
copperlist = AllocMem(100, MEMF_CHIP | MEMF_CLEAR);
bitplane = (APTR *)AllocMem(BITPLANE_SIZE, MEMF_PUBLIC|MEMF_CHIP|MEMF_CLEAR);
// default copperlist has just STOP
((ULONG *)copperlist)[0] = 0xfffffffe;
// set up 1 bitplane pal display
custom.bplcon0 = BPLCON0_COLOR | BPLCON0_BPU_1;
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;
// neither HID button 0 is down
while ((ciaa.ciapra >> 6) == 3) {
// rebuild copperlist
copperlist_ptr = (UWORD *)copperlist;
// bpl1pth
*(copperlist_ptr++) = custom_bplpt;
*(copperlist_ptr++) = ((ULONG)bitplane >> 16) & 0xffff;
// bpl1ptl
*(copperlist_ptr++) = custom_bplpt + 2;
*(copperlist_ptr++) = (ULONG)bitplane & 0xffff;
*(copperlist_ptr++) = custom_color;
*(copperlist_ptr++) = 0x0f00;
// impossible color
currentColor = 0xFFFF;
for (currentY = 0; currentY < 300; currentY++) {
newColor = 0x000;
if (currentY >= redY && currentY < (redY + BAR_HEIGHT)) {
newColor = newColor | 0xf00;
}
if (currentY >= greenY && currentY < (greenY + BAR_HEIGHT)) {
newColor = newColor | 0x0f0;
}
if (currentY >= blueY && currentY < (blueY + BAR_HEIGHT)) {
newColor = newColor | 0x00f;
}
if (newColor != currentColor) {
//printf("Color change at %d: %x to %x\n", currentY, currentColor, newColor);
*(copperlist_ptr++) = ((currentY & 0xff) << 8) | 0x26 | 0x1;
*(copperlist_ptr++) = 0xfffe;
*(copperlist_ptr++) = custom_color;
*(copperlist_ptr++) = newColor;
currentColor = newColor;
}
// PAL workaround
if (currentY == 255) {
*(copperlist_ptr++) = 0xffdf;
*(copperlist_ptr++) = 0xfffe;
}
}
// end the copperlist
*(copperlist_ptr++) = 0xffff;
*(copperlist_ptr++) = 0xfffe;
// advance the bars and loop them around, changing
// speed randomly on every loop
redY += redSpeed;
if (redY > 300) {
redSpeed = (FastRand(randomSeed) + 1) % 5;
randomSeed += redSpeed;
redY %= 300;
}
greenY += greenSpeed;
if (greenY > 300) {
greenSpeed = (FastRand(randomSeed) + 1) % 5;
randomSeed += greenSpeed;
greenY %= 300;
}
blueY += blueSpeed;
if (blueY > 300) {
blueSpeed = (FastRand(randomSeed) + 1) % 5;
randomSeed += blueSpeed;
blueY %= 300;
}
randomSeed %= 0xffffff;
// vbl wait
// this will not work on NTSC!
while ((*custom_vposr & 0x1FF00) != (300 << 8)) {}
}
FreeMem(bitplane, BITPLANE_SIZE);
FreeMem(copperlist, 100);
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();
}

BIN
two columns of lines Normal file

Binary file not shown.

159
two columns of lines.c Normal file
View File

@ -0,0 +1,159 @@
/**
* Two columns of lines demo
* Copyright 2023 John Bintz
* Licensed under the MIT License
* https://theindustriousrabbit.com
*/
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <exec/memory.h>
#include <graphics/GfxBase.h>
#include <hardware/cia.h>
#include <hardware/dmabits.h>
#include <stdio.h>
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;
#define offsetof(s, m) &((struct s *)0)->m
#define BITPLANE_SIZE (320/8)*256
#define BPLCON0_COLOR (0x200)
#define BPLCON0_BPU_1 (0x1000)
static UWORD leftColors[] = {0xf00,0xd00,0xb00,0x900};
static UWORD rightColors[] = {0x00f,0x00d,0x00b,0x009};
int main(void) {
struct View *OldView = ((struct GfxBase *)GfxBase)->ActiView;
ULONG OldCopper = (ULONG)((struct GfxBase *)GfxBase)->copinit;
ULONG OldDMACON, OldINTENA, OldINTREQ, OldADKCON;
int currentY, colorIndex;
void *copperlist;
UWORD *copperlist_ptr;
APTR *bitplane;
// how much can we avoid hard coding into our code?
UWORD custom_bplpt = (UWORD)offsetof(Custom, bplpt);
UWORD custom_color = (UWORD)offsetof(Custom, color);
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
custom.dmacon = DMAF_SETCLR | DMAF_COPPER | DMAF_RASTER;
custom.dmacon = DMAF_AUDIO | DMAF_DISK | DMAF_SPRITE | DMAF_BLITTER;
// 4 bars = WAIT + MOVE (8 bytes) 8 times * 312 + ~100 for setup = 21000 should do it
copperlist = AllocMem(21000, MEMF_CHIP | MEMF_CLEAR);
bitplane = (APTR *)AllocMem(BITPLANE_SIZE, MEMF_PUBLIC|MEMF_CHIP|MEMF_CLEAR);
// default copperlist has just STOP
((ULONG *)copperlist)[0] = 0xfffffffe;
// set up 1 bitplane pal display
custom.bplcon0 = BPLCON0_COLOR | BPLCON0_BPU_1;
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;
// neither HID button 0 is down
while ((ciaa.ciapra >> 6) == 3) {
// rebuild copperlist
copperlist_ptr = (UWORD *)copperlist;
// bpl1pth
*(copperlist_ptr++) = custom_bplpt;
*(copperlist_ptr++) = ((ULONG)bitplane >> 16) & 0xffff;
// bpl1ptl
*(copperlist_ptr++) = custom_bplpt + 2;
*(copperlist_ptr++) = (ULONG)bitplane & 0xffff;
*(copperlist_ptr++) = custom_color;
*(copperlist_ptr++) = leftColors[0];
for (currentY = 0; currentY < 312; currentY++) {
colorIndex = (currentY / 4) % 4;
*(copperlist_ptr++) = ((currentY & 0xff) << 8) | 0x06 | 0x1;
*(copperlist_ptr++) = 0xfffe;
*(copperlist_ptr++) = custom_color;
*(copperlist_ptr++) = leftColors[colorIndex];
*(copperlist_ptr++) = ((currentY & 0xff) << 8) | 0x90 | 0x1;
*(copperlist_ptr++) = 0xfffe;
*(copperlist_ptr++) = custom_color;
*(copperlist_ptr++) = rightColors[colorIndex];
// PAL workaround
if (currentY == 255) {
*(copperlist_ptr++) = 0xffdf;
*(copperlist_ptr++) = 0xfffe;
}
}
// end the copperlist
*(copperlist_ptr++) = 0xffff;
*(copperlist_ptr++) = 0xfffe;
// vbl wait
// this will not work on NTSC!
while ((*custom_vposr & 0x1FF00) != (300 << 8)) {}
}
FreeMem(bitplane, BITPLANE_SIZE);
FreeMem(copperlist, 21000);
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();
}