diff --git a/README.md b/README.md index ecae2c5..a12abe8 100644 --- a/README.md +++ b/README.md @@ -672,6 +672,23 @@ Draws filled random circles and fills the space using flood fill. Similar to its hires counterpart but using four shades of grey. +### Particle systems "particles" + +Active hires graphics using particle system + +#### Hires fireworks "fireworks_hires.c" + +Simple hires particle system using velocity and gravity integration. + +#### Multicolor fireworks "fireworks_ptr.c" + +Simple multi color hires particle system using velocity and gravity integration. + +#### Fireworks with striped memory layout "fireworks_stripe.c" + +Simple multi color hires particle system using velocity and gravity integration. This version uses striped memory layout for the particles to simplify addressing for the CPU. + + ### Mandelbrot renderer "fractals" Various versions of the mandelbrot set using float arithmetic. @@ -694,6 +711,10 @@ Multi color version using pure and mixed colors. Mandelbrot rendered in 3D with shading. The image is drawn in columns from back to front, using two adjacent columns to calculate slope and brightness. +#### Interactive Navigate a fractal "mbzoom.c" + +Navigate using WASD and zoom using + and -. + ### Raster beam interrupts "rasterirq" diff --git a/oscar64/InterCode.cpp b/oscar64/InterCode.cpp index 4c13164..053f1f2 100644 --- a/oscar64/InterCode.cpp +++ b/oscar64/InterCode.cpp @@ -8792,24 +8792,24 @@ void InterCodeBasicBlock::MarkRelevantStatics(void) if (ins->mCode == IC_LOAD) { if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mMemory == IM_GLOBAL) - ins->mSrc[0].mLinkerObject->mFlags |= LOBJF_RELEVANT; + ins->mSrc[0].mLinkerObject->MarkRelevant(); } else if (ins->mCode == IC_LEA) { if (ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_GLOBAL) - ins->mSrc[1].mLinkerObject->mFlags |= LOBJF_RELEVANT; + ins->mSrc[1].mLinkerObject->MarkRelevant(); } else if (ins->mCode == IC_CONSTANT && ins->mDst.mType == IT_POINTER) { if (ins->mConst.mMemory == IM_GLOBAL) - ins->mConst.mLinkerObject->mFlags |= LOBJF_RELEVANT; + ins->mConst.mLinkerObject->MarkRelevant(); } else if (ins->mCode == IC_COPY || ins->mCode == IC_STRCPY) { if (ins->mSrc[0].mTemp < 0 && ins->mSrc[0].mMemory == IM_GLOBAL) - ins->mSrc[0].mLinkerObject->mFlags |= LOBJF_RELEVANT; + ins->mSrc[0].mLinkerObject->MarkRelevant(); if (ins->mSrc[1].mTemp < 0 && ins->mSrc[1].mMemory == IM_GLOBAL) - ins->mSrc[1].mLinkerObject->mFlags |= LOBJF_RELEVANT; + ins->mSrc[1].mLinkerObject->MarkRelevant(); } } @@ -13018,12 +13018,12 @@ void InterCodeProcedure::RemoveUnusedStoreInstructions(InterMemory paramMemory) { if (mLocalVars.Size() > 0 || mParamVars.Size() > 0) { - for (int i = 0; i < mLocalVars.Size(); i++) + for (int i = 0; i < mLocalAliasedSet.Size(); i++) { if (mLocalAliasedSet[i]) mLocalVars[i]->mAliased = true; } - for (int i = 0; i < mParamVars.Size(); i++) + for (int i = 0; i < mParamAliasedSet.Size(); i++) { if (mParamAliasedSet[i]) mParamVars[i]->mAliased = true; diff --git a/oscar64/Linker.cpp b/oscar64/Linker.cpp index a6bdacb..cc416e3 100644 --- a/oscar64/Linker.cpp +++ b/oscar64/Linker.cpp @@ -43,6 +43,17 @@ void LinkerObject::AddReference(const LinkerReference& ref) mReferences.Push(nref); } +void LinkerObject::MarkRelevant(void) +{ + if (!(mFlags & LOBJF_RELEVANT)) + { + mFlags |= LOBJF_RELEVANT; + for (int i = 0; i < mReferences.Size(); i++) + if (mReferences[i]->mRefObject) + mReferences[i]->mRefObject->MarkRelevant(); + } +} + void LinkerObject::MoveToSection(LinkerSection* section) { if (section != mSection) diff --git a/oscar64/Linker.h b/oscar64/Linker.h index 66bda30..dda3f2a 100644 --- a/oscar64/Linker.h +++ b/oscar64/Linker.h @@ -167,6 +167,8 @@ public: void AddReference(const LinkerReference& ref); void MoveToSection(LinkerSection* section); + + void MarkRelevant(void); }; class Linker diff --git a/oscar64/NativeCodeGenerator.cpp b/oscar64/NativeCodeGenerator.cpp index ba6f949..0ae4f01 100644 --- a/oscar64/NativeCodeGenerator.cpp +++ b/oscar64/NativeCodeGenerator.cpp @@ -3038,9 +3038,16 @@ bool NativeCodeInstruction::ValueForwarding(NativeRegisterDataSet& data, AsmInsT #if 1 if (mMode == ASMIM_ABSOLUTE_X && data.mRegs[CPU_REG_X].SameData(data.mRegs[CPU_REG_Y]) && HasAsmInstructionMode(mType, ASMIM_ABSOLUTE_Y) && !(mFlags & NICT_INDEXFLIPPED)) { + mFlags |= NICT_INDEXFLIPPED; mMode = ASMIM_ABSOLUTE_Y; changed = true; } + else if (mMode == ASMIM_ABSOLUTE_Y && data.mRegs[CPU_REG_X].SameData(data.mRegs[CPU_REG_Y]) && HasAsmInstructionMode(mType, ASMIM_ABSOLUTE_X) && !(mFlags & NICT_INDEXFLIPPED)) + { + mFlags |= NICT_INDEXFLIPPED; + mMode = ASMIM_ABSOLUTE_X; + changed = true; + } #endif if (mMode == ASMIM_ZERO_PAGE) { @@ -20966,6 +20973,23 @@ bool NativeCodeBasicBlock::ValueForwarding(const NativeRegisterDataSet& data, bo { AsmInsType carryop; +#if 1 + if (mIns[i].mMode == ASMIM_INDIRECT_Y && HasAsmInstructionMode(mIns[i].mType, ASMIM_ABSOLUTE_Y) && + mNDataSet.mRegs[CPU_REG_Y].mMode == NRDM_IMMEDIATE && + mNDataSet.mRegs[mIns[i].mAddress + 1].mMode == NRDM_IMMEDIATE_ADDRESS && (mNDataSet.mRegs[mIns[i].mAddress + 1].mFlags & NCIF_UPPER) && + mNDataSet.mRegs[mIns[i].mAddress].mMode != NRDM_IMMEDIATE_ADDRESS) + { + int reg = mIns[i].mAddress; + mIns.Insert(i, NativeCodeInstruction(ASMIT_LDY, ASMIM_ZERO_PAGE, reg)); + mIns[i + 1].mMode = ASMIM_ABSOLUTE_Y; + mIns[i + 1].mLinkerObject = mNDataSet.mRegs[reg + 1].mLinkerObject; + mIns[i + 1].mAddress = mNDataSet.mRegs[reg + 1].mValue + mNDataSet.mRegs[CPU_REG_Y].mValue; + mIns[i + 1].mFlags |= NCIF_LOWER; + mIns.Insert(i + 2, NativeCodeInstruction(ASMIT_LDY, ASMIM_IMMEDIATE, mNDataSet.mRegs[CPU_REG_Y].mValue)); + mIns.Insert(i + 3, NativeCodeInstruction(ASMIT_ORA, ASMIM_IMMEDIATE, 0)); + changed = true; + } +#endif // Check load and commutative with current accu value #if 1 if (i + 1 < mIns.Size() && mIns[i].mType == ASMIT_LDA && mIns[i + 1].IsCommutative() && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mNDataSet.mRegs[CPU_REG_A].mMode == NRDM_ZERO_PAGE && mNDataSet.mRegs[CPU_REG_A].mValue == mIns[i + 1].mAddress) @@ -29339,21 +29363,19 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass } #endif -#if 1 - else if ( - mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 1].mFlags & NCIF_UPPER) && - mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && - !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) + } + + if (i + 1 < mIns.Size() && + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE_ADDRESS && (mIns[i + 0].mFlags & NCIF_UPPER) && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE) + { + proc->ResetPatched(); + if (CheckGlobalAddressSumYPointer(this, mIns[i + 1].mAddress - 1, mIns[i + 1].mAddress - 1, i + 2, -1)) { proc->ResetPatched(); - if (CheckGlobalAddressSumYPointer(this, mIns[i + 1].mAddress - 1, mIns[i + 1].mAddress - 1, i + 2, -1)) - { - proc->ResetPatched(); - if (PatchGlobalAddressSumYPointer(this, mIns[i + 1].mAddress - 1, mIns[i + 1].mAddress - 1, i + 2, -1, mIns[i + 0].mLinkerObject, mIns[i + 0].mAddress)) - progress = true; - } + if (PatchGlobalAddressSumYPointer(this, mIns[i + 1].mAddress - 1, mIns[i + 1].mAddress - 1, i + 2, -1, mIns[i + 0].mLinkerObject, mIns[i + 0].mAddress)) + progress = true; } -#endif } if (i + 5 < mIns.Size() && diff --git a/samples/build.sh b/samples/build.sh index 053de8e..c7a03bf 100644 --- a/samples/build.sh +++ b/samples/build.sh @@ -15,6 +15,10 @@ cd hiresmc ./build.sh cd .. +cd particles +./build.sh +cd .. + cd kernalio ./build.sh cd .. diff --git a/samples/fractals/build.sh b/samples/fractals/build.sh index 1be9907..8cc01b6 100644 --- a/samples/fractals/build.sh +++ b/samples/fractals/build.sh @@ -4,4 +4,5 @@ ../../bin/oscar64 mbmulti.c -n ../../bin/oscar64 mbmulti3d.c -n ../../bin/oscar64 mbfixed.c -n -O3 +../../bin/oscar64 mbzoom.c -n -O3 diff --git a/samples/fractals/make.bat b/samples/fractals/make.bat index 86f59e0..730f50e 100644 --- a/samples/fractals/make.bat +++ b/samples/fractals/make.bat @@ -3,3 +3,4 @@ call ..\..\bin\oscar64 mbhires.c -n call ..\..\bin\oscar64 mbmulti.c -n call ..\..\bin\oscar64 mbmulti3d.c -n call ..\..\bin\oscar64 mbfixed.c -n -O3 +call ..\..\bin\oscar64 mbzoom.c -n -O3 diff --git a/samples/fractals/mbzoom.c b/samples/fractals/mbzoom.c new file mode 100644 index 0000000..3ea8ab0 --- /dev/null +++ b/samples/fractals/mbzoom.c @@ -0,0 +1,293 @@ +#include +#include +#include + +#define Screen ((char *)0x0400) +#define Color ((char *)0xd800) + +// Lookup table for squares from 0..255 +__striped unsigned sqb[256]; + +#pragma align(sqb, 256); + +// Square an unsigned int into an unsigned long +inline unsigned long ssquare(unsigned x) +{ + // Split into low byte and highbyte, so we have x = a + 0x100 * b + + unsigned a = x & 0xff; + unsigned b = x >> 8; + + // So now we calculate (a + 0x100 * b)² + // Result will be a² + 0x100 * 2 * a * b + 0x10000 * b² + // with 2 * a * b == (a + b)² - a² - b² + + // We can cover all cases with the square table except if a + b >= 0x100 + // in this case we have abp := a + b - 0x100 + // (abp + 0x100)² == abp² + 2 * 0x100 * abp + 0x10000 + + // Get squares of the bytes and the sum of the bytes + unsigned a2 = sqb[a], b2 = sqb[b]; + unsigned apb = a + b; + + // First approximation approximation + // a² + 0x10000 * b² + unsigned long sum = a2 + ((unsigned long)b2 << 16); + + // Check if a + b >= 0x100 + if (apb & 0xff00) + { + apb &= 0xff; + sum += 0x1000000UL; + sum += (unsigned long)apb << 17; + sum += (unsigned long)sqb[apb] << 8; + } + else + { + apb &= 0xff; + sum += (unsigned long)sqb[apb] << 8; + } + + // Now w have a² + 0x1000 * b² + (a + b)² + + sum -= (unsigned long)a2 << 8; + sum -= (unsigned long)b2 << 8; + + // And finaly the complete result + return sum; +} + +// Signed square of x +inline long sq(int x) +{ + if (x < 0) + x = -x; + return ssquare(x); +} + +// Colors to fill in the different levels +static const char colors[32] = { + VCOL_BLUE, + VCOL_LT_BLUE, + VCOL_WHITE, + VCOL_LT_GREEN, + VCOL_GREEN, + VCOL_YELLOW, + VCOL_ORANGE, + VCOL_RED, + VCOL_PURPLE, + + VCOL_BLUE, + VCOL_BLUE, + VCOL_LT_BLUE, + VCOL_LT_BLUE, + VCOL_WHITE, + VCOL_WHITE, + VCOL_LT_GREEN, + VCOL_LT_GREEN, + VCOL_GREEN, + VCOL_GREEN, + VCOL_YELLOW, + VCOL_YELLOW, + VCOL_ORANGE, + VCOL_ORANGE, + VCOL_RED, + VCOL_RED, + VCOL_PURPLE, + VCOL_PURPLE, + + VCOL_LT_GREY, + VCOL_LT_GREY, + VCOL_MED_GREY, + VCOL_MED_GREY, + VCOL_DARK_GREY, +}; + +// Return color for a given coordinate in the complex plane using +// 12.4bit fixed numbers using m'=m²+b + +inline char fcolor(int xz, int yz) +{ + // Start value for iteration is the offset value itself + + int x = xz, y = yz; + + // Iterate up to 32 steps + + for(int i=0; i<32; i++) + { + // Build squares of real and imaginery component + long xx = sq(x), yy = sq(y), xxyy = sq(x + y); + + // Use squares to check for exit condition of sure + // to proress towards infinity + if (xx + yy >= 4L * 4096 * 4096) return colors[i]; + + // Next iteration values using complex artithmetic + // Mx' = Mx² - My² + Bx + // My' = 2 * Mx * My + By = (Mx + My)² - Mx² - My² + By + x = ((xx - yy + 2048) >> 12) + xz; + y = ((xxyy - xx - yy + 2048) >> 12) + yz; + } + + // More than maximum number of iterations, so assume progress + // towards zero + + return VCOL_BLACK; +} + +// Fill a row with color +void fill_row(char py, int cix, int yz, int cis) +{ + int xz = cix; + for(int px=0; px<40; px++) + { + Color[py * 40 + px] = fcolor(xz, yz); + xz += cis; + } +} + +// Fill a column with color +void fill_column(char px, int xz, int ciy, int cis) +{ + int yz = ciy; + for(int py=0; py<25; py++) + { + Color[py * 40 + px] = fcolor(xz, yz); + yz += cis; + } +} + +// Fill the complete image +void fill_image(int cix, int ciy, int cis) +{ + int yz = ciy; + for(int py=0; py<25; py++) + { + fill_row(py, cix, yz, cis); + yz += cis; + } +} + +// Scroll screen to the left +void scroll_left(void) +{ + for(char x=0; x<39; x++) + { + #pragma unroll(full) + for(char y=0; y<25; y++) + { + Color[y * 40 + x] = Color[y * 40 + x + 1]; + } + } +} + +// Scroll screen to the right +void scroll_right(void) +{ + for(signed char x=38; x>=0; x--) + { + #pragma unroll(full) + for(char y=0; y<25; y++) + { + Color[y * 40 + x + 1] = Color[y * 40 + x]; + } + } +} + +// Scroll screen up +void scroll_up(void) +{ + for(char x=0; x<40; x++) + { + #pragma unroll(full) + for(char y=0; y<24; y++) + { + Color[y * 40 + x] = Color[(y + 1) * 40 + x]; + } + } +} + +// Scroll screen down +void scroll_down(void) +{ + for(char x=0; x<40; x++) + { + #pragma unroll(full) + for(char y=0; y<24; y++) + { + Color[(24 - y) * 40 + x] = Color[(23 - y) * 40 + x]; + } + } +} + +int main(void) +{ + // Initialize square table + for(unsigned i=0; i<256; i++) + sqb[i] = i * i; + + // Clear screen + memset(Screen, 160, 1024); + + // Start coordinates in float + float cx = -0.4; + float cy = 0.0; + float cw = 3.2; + + // Convert to top, left and step in 12.4 fixed point + int cix = (int)((cx - 0.5 * cw) * 4096); + int ciy = (int)((cy - 12.0 * cw / 40.0) * 4096); + int cis = (int)(cw / 40.0 * 4096); + + // Initial image + fill_image(cix, ciy, cis); + + for(;;) + { + // Wait for keypress + char ch = getch(); + + switch (ch) + { + case 'S': + ciy += cis; + scroll_up(); + fill_row(24, cix, ciy + 24 * cis, cis); + break; + case 'W': + ciy -= cis; + scroll_down(); + fill_row(0, cix, ciy, cis); + break; + case 'A': + cix -= cis; + scroll_right(); + fill_column(0, cix, ciy, cis); + break; + case 'D': + cix += cis; + scroll_left(); + fill_column(39, cix + 39 * cis, ciy, cis); + break; + case '+': + cix += 20 * cis; + ciy += 12 * cis; + cis = cis * 2 / 3; + cix -= 20 * cis; + ciy -= 12 * cis; + fill_image(cix, ciy, cis); + break; + case '-': + cix += 20 * cis; + ciy += 12 * cis; + cis = cis * 3 / 2; + cix -= 20 * cis; + ciy -= 12 * cis; + fill_image(cix, ciy, cis); + break; + } + } + + return 0; +} diff --git a/samples/make.bat b/samples/make.bat index 3052882..987fa84 100644 --- a/samples/make.bat +++ b/samples/make.bat @@ -14,6 +14,10 @@ cd hiresmc call make.bat cd .. +cd particles +call make.bat +cd .. + cd kernalio call make.bat cd .. diff --git a/samples/particles/build.sh b/samples/particles/build.sh new file mode 100644 index 0000000..1fdf97a --- /dev/null +++ b/samples/particles/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh +../../bin/oscar64 -n fireworks_ptr.c +../../bin/oscar64 -n fireworks_hires.c +../../bin/oscar64 -n fireworks_stripe.c + diff --git a/samples/particles/fireworks_hires.c b/samples/particles/fireworks_hires.c new file mode 100644 index 0000000..1ce3f5d --- /dev/null +++ b/samples/particles/fireworks_hires.c @@ -0,0 +1,215 @@ +#include +#include +#include +#include + +static char * const Screen = (char *)0xc800; +static char * const Color = (char *)0xd800; +static char * const Hires = (char *)0xe000; + +// Single particle, with position and veloicty, using a next +// pointer for single linked list +struct Particle +{ + int px, py, vx, vy; + Particle * next; +}; + +// Storage for up to 256 particles +Particle particles[256]; + +// Heads of used and free list +Particle * pfirst, * pfree; + +// Lookup table for hires row buffer +static char * Hirows[25]; + +// Pixel masks +static const char setmask[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; +static const char clrmask[8] = {0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe}; + +// Set a pixel at the given coordiate +void pix_set(unsigned px, unsigned py) +{ + // Give the compiler a hand + __assume(px < 320); + __assume(py < 200); + + // Calculate base position in hires + char * dp = Hirows[py >> 3] + (px & ~7); + + // Set the pixel + dp[py & 7] |= setmask[px & 7]; +} + +// Clear a pixel at the given coordiate +void pix_clr(unsigned px, unsigned py) +{ + __assume(px < 320); + __assume(py < 200); + + // Calculate base position in hires + char * dp = Hirows[py >> 3] + (px & ~7); + + // Clear the pixel + dp[py & 7] &= clrmask[px & 7]; +} + +// Init free list of particles +void particle_init(void) +{ + // Init address table for hires + for(int i=0; i<25; i++) + Hirows[i] = Hires + 320 * i; + + // Init list heads + pfirst = nullptr; + pfree = particles; + + // Link all particles in free list + for(int i=0; i<255; i++) + particles[i].next = particles + i + 1; +} + +// Add a particle to the list +void particle_add(int px, int py, int vx, int vy) +{ + // Check if we have a particle left + if (pfree) + { + // Remove from free list + Particle * p = pfree; + pfree = pfree->next; + + // Add to used list + p->next = pfirst; + pfirst = p; + + // Init particle data + p->px = px; + p->py = py; + p->vx = vx; + p->vy = vy; + } +} + +// Move particles in used list +void particle_move(void) +{ + // Start with first particle, remember previous + // particle for list removal + Particle * p = pfirst, * pp = nullptr; + + // Loop over all particles in used list + while (p) + { + // Clear previous particle image, using 10.6 fixed point + pix_clr(p->px >> 6, p->py >> 6); + + // Advance position by velocity + p->px += p->vx; + p->py += p->vy; + + // Apply gravity + p->vy += 8; + + // Check if particle is still on screen + if (p->px < 0 || p->px >= 320 * 64 || p->py < 0 || p->py >= 200 * 64) + { + // Particle is offscreen, so we remove it from the used list + + // Remember next particle in used list + Particle * pn = p->next; + + // Remove from used list + if (pp) + pp->next = pn; + else + pfirst = pn; + + // Attach to free list + p->next = pfree; + pfree = p; + + // Advance to next particle + p = pn; + } + else + { + // Set image at new position + pix_set(p->px >> 6, p->py >> 6); + + // Advance to next particle + pp = p; + p = p->next; + } + } +} + +// Normalized random function +int rnorm(void) +{ + int l0 = (rand() & 0xfff) - 0x800; + int l1 = (rand() & 0xfff) - 0x800; + int l2 = (rand() & 0xfff) - 0x800; + int l3 = (rand() & 0xfff) - 0x800; + + return l0 + l1 + l2 + l3; +} + +int main(void) +{ + // Turn off BASIC ROM + mmap_set(MMAP_NO_BASIC); + + // Install IRQ trampoline + mmap_trampoline(); + + // Turn off kernal ROM + mmap_set(MMAP_NO_ROM); + + // Switch to hires mode + vic_setmode(VICM_HIRES, Screen, Hires); + + // Clear screen + memset(Screen, 0x10, 1000); + memset(Hires, 0x00, 8000); + + // Black background + vic.color_border = 0x00; + vic.color_back = 0x00; + + // Init particle system + particle_init(); + + char k = 0; + for(int i=0; i<10000; i++) + { + // Advance particles + particle_move(); + + if (k < 25) + { + // Add a particle from the left for the first third + particle_add(4 * 64, 196 * 64, 256 + (rnorm() >> 6), -(384 + (rnorm() >> 6))); + } + else if (k < 50) + { + // Add a particle from the right for the second third + particle_add(316 * 64, 196 * 64, - (256 + (rnorm() >> 6)), -(384 + (rnorm() >> 6))); + } + else if (k < 75) + { + // Add a particle from the middle for the final third + particle_add(160 * 64, 196 * 64, rnorm() >> 6, -(384 + (rnorm() >> 6))); + } + + // Advance thirds counter + k++; + if (k == 75) + k = 0; + } + + return 0; + +} diff --git a/samples/particles/fireworks_ptr.c b/samples/particles/fireworks_ptr.c new file mode 100644 index 0000000..afd2672 --- /dev/null +++ b/samples/particles/fireworks_ptr.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include + +static char * const Screen = (char *)0xc800; +static char * const Color = (char *)0xd800; +static char * const Hires = (char *)0xe000; + +// Single particle, with position, veloicty and color pattern, using a next +// pointer for single linked list +struct Particle +{ + int px, py, vx, vy; + char pat; + Particle * next; +}; + +// Storage for up to 256 particles +Particle particles[256]; + +// Heads of used and free list +Particle * pfirst, * pfree; + +// Lookup table for hires row buffer +static char * Hirows[25]; + +// Pixel masks +static const char setmask[8] = {0xc0, 0xc0, 0x30, 0x30, 0x0c, 0x0c, 0x03, 0x03}; +static const char clrmask[8] = {0x3f, 0x3f, 0xcf, 0xcf, 0xf3, 0xf3, 0xfc, 0xfc}; + +// Set a pixel at the given coordiate +void pix_set(unsigned px, unsigned py, char pat) +{ + __assume(px < 320); + __assume(py < 200); + + // Calculate base position in hires + char * dp = Hirows[py >> 3] + (px & ~7); + + // Set two pixels for a square pixel look + char ly = py & 6; + dp[ly + 1] = dp[ly + 0] |= setmask[px & 7] & pat; +} + +// Clear a pixel at the given coordiate +void pix_clr(unsigned px, unsigned py) +{ + __assume(px < 320); + __assume(py < 200); + + // Calculate base position in hires + char * dp = Hirows[py >> 3] + (px & ~7); + + // Clear two pixels for a square pixel look + char ly = py & 6; + dp[ly + 1] = dp[ly + 0] &= clrmask[px & 7]; +} + +// Init free list of particles +void particle_init(void) +{ + // Init address table for hires + for(int i=0; i<25; i++) + Hirows[i] = Hires + 320 * i; + + // Init list heads + pfirst = nullptr; + pfree = particles; + + // Link all particles in free list + for(int i=0; i<255; i++) + particles[i].next = particles + i + 1; +} + +// Add a particle to the list +void particle_add(int px, int py, int vx, int vy, char pat) +{ + // Check if we have a particle left + if (pfree) + { + // Remove from free list + Particle * p = pfree; + pfree = pfree->next; + + // Add to used list + p->next = pfirst; + pfirst = p; + + // Init particle data + p->px = px; + p->py = py; + p->vx = vx; + p->vy = vy; + p->pat = pat; + } +} + +// Move particles in used list +void particle_move(void) +{ + // Start with first particle, remember previous + // particle for list removal + Particle * p = pfirst, * pp = nullptr; + + // Loop over all particles in used list + while (p) + { + // Clear previous particle image, using 10.6 fixed point + pix_clr(p->px >> 6, p->py >> 6); + + // Advance position by velocity + p->px += p->vx; + p->py += p->vy; + + // Apply gravity + p->vy += 8; + + // Check if particle is still on screen + if (p->px < 0 || p->px >= 320 * 64 || p->py < 0 || p->py >= 200 * 64) + { + // Particle is offscreen, so we remove it from the used list + + // Remember next particle in used list + Particle * pn = p->next; + + // Remove from used list + if (pp) + pp->next = pn; + else + pfirst = pn; + + // Attach to free list + p->next = pfree; + pfree = p; + + // Advance to next particle + p = pn; + } + else + { + // Set image at new position + pix_set(p->px >> 6, p->py >> 6, p->pat); + + // Advance to next particle + pp = p; + p = p->next; + } + } +} + +// Normalized random function +int rnorm(void) +{ + int l0 = (rand() & 0xfff) - 0x800; + int l1 = (rand() & 0xfff) - 0x800; + int l2 = (rand() & 0xfff) - 0x800; + int l3 = (rand() & 0xfff) - 0x800; + + return l0 + l1 + l2 + l3; +} + +int main(void) +{ + // Turn off BASIC ROM + mmap_set(MMAP_NO_BASIC); + + // Install IRQ trampoline + mmap_trampoline(); + + // Turn off kernal ROM + mmap_set(MMAP_NO_ROM); + + // Switch to hires multicolor mode + vic_setmode(VICM_HIRES_MC, Screen, Hires); + + // Clear screen + memset(Screen, 0x78, 1000); + memset(Color, 0x0e, 1000); + memset(Hires, 0x00, 8000); + + // Black background + vic.color_border = 0x00; + vic.color_back = 0x00; + + // Init particle system + particle_init(); + + char k = 0; + for(int i=0; i<10000; i++) + { + // Advance particles + particle_move(); + + if (k < 25) + { + // Add a particle from the left for the first third + particle_add(4 * 64, 196 * 64, 256 + (rnorm() >> 6), -(384 + (rnorm() >> 6)), 0x55); + } + else if (k < 50) + { + // Add a particle from the right for the second third + particle_add(316 * 64, 196 * 64, - (256 + (rnorm() >> 6)), -(384 + (rnorm() >> 6)), 0xaa); + } + else if (k < 75) + { + // Add a particle from the middle for the final third + particle_add(160 * 64, 196 * 64, rnorm() >> 6, -(384 + (rnorm() >> 6)), 0xff); + } + + // Advance thirds counter + k++; + if (k == 75) + k = 0; + } + + return 0; + +} diff --git a/samples/particles/fireworks_stripe.c b/samples/particles/fireworks_stripe.c new file mode 100644 index 0000000..fcaad75 --- /dev/null +++ b/samples/particles/fireworks_stripe.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include + +static char * const Screen = (char *)0xc800; +static char * const Color = (char *)0xd800; +static char * const Hires = (char *)0xe000; + + +// Single particle, with position, veloicty and color pattern, using a next +// index for single linked list +struct Particle +{ + int px, py, vx, vy; + char pat; + char next; +}; + +// Striped storage of particles, using an index for linkage +__striped Particle particles[256]; + +#pragma align(particles, 256) + +// Index for used and free list heads +char pfirst, pfree; + +static char * Hirows[25]; + +static const char setmask[4] = {0xc0, 0x30, 0x0c, 0x03}; +static const char clrmask[4] = {0x3f, 0xcf, 0xf3, 0xfc}; + +// Set a pixel at the given coordiate +void pix_set(char px, char py, char pat) +{ + __assume(px < 160); + __assume(py < 100); + + // Calculate base position in hires + char * dp = Hirows[py >> 2] + 2 * (px & ~3); + + // Set two pixels for a square pixel look + char ly = 2 * (py & 3); + dp[ly + 1] = dp[ly + 0] |= setmask[px & 3] & pat; +} + +// Clear a pixel at the given coordiate +void pix_clr(char px, char py) +{ + __assume(px < 160); + __assume(py < 100); + + // Calculate base position in hires + char * dp = Hirows[py >> 2] + 2 * (px & ~3); + + // Clear two pixels for a square pixel look + char ly = 2 * (py & 3); + dp[ly + 1] = dp[ly + 0] &= clrmask[px & 3]; +} + +// Init free list of particles +void particle_init(void) +{ + // Init address table for hires + for(int i=0; i<25; i++) + Hirows[i] = Hires + 320 * i; + + // Init list heads, using index 0 for list termination + pfirst = 0; + pfree = 1; + + // Link all particles in free list + for(int i=1; i<255; i++) + particles[i].next = i + 1; +} + +// Add a particle to the list +void particle_add(int px, int py, int vx, int vy, char pat) +{ + // Check if we have a particle left + if (pfree) + { + // Use "auto" to generate a striped pointer + char i = pfree; + auto p = particles + pfree; + + // Remove from free list + pfree = p->next; + p->next = pfirst; + + // Add to used list + pfirst = i; + + // Init particle data + p->px = px; + p->py = py; + p->vx = vx; + p->vy = vy; + p->pat = pat; + } +} + +// Move particles in used list +void particle_move(void) +{ + // Start with first particle, remember previous + // particle for list removal, using indices instead of pointers + char i = pfirst, pi = 0; + + // Zero is still list termination + while (i) + { + // Use "auto" to generate a striped pointer + auto p = particles + i; + + // Clear previous particle image, using 9.7 fixed point + pix_clr(p->px >> 7, p->py >> 7); + + // Advance position by velocity + p->px += p->vx; + p->py += p->vy; + + // Apply gravity + p->vy += 8; + + // Check if particle is still on screen + if (p->px < 0 || p->px >= 160 * 128 || p->py < 0 || p->py >= 100 * 128) + { + // Particle is offscreen, so we remove it from the used list + + // Remember next particle in used list + char pn = p->next; + + // Remove from used list + if (pi) + particles[pi].next = pn; + else + pfirst = pn; + + // Attach to free list + p->next = pfree; + pfree = i; + + // Advance to next particle + i = pn; + } + else + { + // Set image at new position + pix_set(p->px >> 7, p->py >> 7, p->pat); + + // Advance to next particle + pi = i; + i = p->next; + } + } +} + +// Normalized random function +int rnorm(void) +{ + int l0 = (rand() & 0xfff) - 0x800; + int l1 = (rand() & 0xfff) - 0x800; + int l2 = (rand() & 0xfff) - 0x800; + int l3 = (rand() & 0xfff) - 0x800; + + return l0 + l1 + l2 + l3; +} + +int main(void) +{ + // Turn off BASIC ROM + mmap_set(MMAP_NO_BASIC); + + // Install IRQ trampoline + mmap_trampoline(); + + // Turn off kernal ROM + mmap_set(MMAP_NO_ROM); + + // Switch to hires multicolor mode + vic_setmode(VICM_HIRES_MC, Screen, Hires); + + // Clear screen + memset(Screen, 0x78, 1000); + memset(Color, 0x0e, 1000); + memset(Hires, 0x00, 8000); + + // Black background + vic.color_border = 0x00; + vic.color_back = 0x00; + + // Init particle system + particle_init(); + + char k = 0; + for(int i=0; i<10000; i++) + { + // Advance particles + particle_move(); + + if (k < 25) + { + // Add a particle from the left for the first third + particle_add(4 * 64, 196 * 64, 256 + (rnorm() >> 6), -(384 + (rnorm() >> 6)), 0x55); + } + else if (k < 50) + { + // Add a particle from the right for the second third + particle_add(316 * 64, 196 * 64, - (256 + (rnorm() >> 6)), -(384 + (rnorm() >> 6)), 0xaa); + } + else if (k < 75) + { + // Add a particle from the middle for the final third + particle_add(160 * 64, 196 * 64, rnorm() >> 6, -(384 + (rnorm() >> 6)), 0xff); + } + + // Advance thirds counter + k++; + if (k == 75) + k = 0; + } + + return 0; + +} diff --git a/samples/particles/make.bat b/samples/particles/make.bat new file mode 100644 index 0000000..dbba9e8 --- /dev/null +++ b/samples/particles/make.bat @@ -0,0 +1,4 @@ +call ..\..\bin\oscar64 -n fireworks_ptr.c +call ..\..\bin\oscar64 -n fireworks_hires.c +call ..\..\bin\oscar64 -n fireworks_stripe.c +