From 995e39a57dd96413d49f05f8af044e13794ddb73 Mon Sep 17 00:00:00 2001 From: drmortalwombat <90205530+drmortalwombat@users.noreply.github.com> Date: Mon, 31 Jan 2022 21:17:14 +0100 Subject: [PATCH] Add missile command sample game --- include/gfx/mcbitmap.c | 5 +- oscar64/NativeCodeGenerator.cpp | 53 ++- oscar64/oscar64.cpp | 2 +- oscar64/oscar64.rc | 8 +- oscar64setup/oscar64setup.vdproj | 32 +- samples/games/build.sh | 1 + samples/games/lander.c | 6 +- samples/games/make.bat | 2 +- samples/games/maze3d.c | 16 +- samples/games/missile.c | 638 ++++++++++++++++++++++++++----- samples/games/snake.c | 4 +- samples/memmap/easyflash.crt | Bin 114976 -> 114976 bytes samples/scrolling/cgrid8way.c | 10 +- samples/sprites/joycontrol.c | 6 +- 14 files changed, 640 insertions(+), 143 deletions(-) diff --git a/include/gfx/mcbitmap.c b/include/gfx/mcbitmap.c index 2d85eeb..0385545 100644 --- a/include/gfx/mcbitmap.c +++ b/include/gfx/mcbitmap.c @@ -34,9 +34,10 @@ char MixedColors[4][4][8] = { void bmmc_put(const Bitmap * bm, int x, int y, char c) { - char * dp = bm->data + bm->cwidth * (y & ~7) + ((x & ~7) | (y & 7)); + char * dp = bm->data + bm->cwidth * (y & ~7) + ((x & ~7) | (y & 7)); + char pat = cbytes[c & 3]; - *dp = (*dp & andmask[x & 7]) | (cbytes[c & 3] & ormask[x & 7]); + *dp = ((*dp ^ pat) & andmask[x & 7]) ^ pat; } char bmmc_get(const Bitmap * bm, int x, int y) diff --git a/oscar64/NativeCodeGenerator.cpp b/oscar64/NativeCodeGenerator.cpp index f8f8f61..78322bb 100644 --- a/oscar64/NativeCodeGenerator.cpp +++ b/oscar64/NativeCodeGenerator.cpp @@ -11987,7 +11987,7 @@ void NativeCodeBasicBlock::BlockSizeReduction(void) i += 3; } else if (i + 5 < mIns.Size() && - mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 0].ChangesAccuAndFlag() && mIns[i + 1].mType == ASMIT_CMP && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0x01 && mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress == 0x00 && mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 0xff && @@ -13032,7 +13032,8 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) } else mIns.Insert(i + 0, NativeCodeInstruction(ASMIT_LDY, ASMIM_ZERO_PAGE, iins->mAddress)); - mIns[i + 0].mLive = mIns[i - 1].mLive | LIVE_CPU_REG_Y | LIVE_MEM; + if (i > 0) + mIns[i + 0].mLive = mIns[i - 1].mLive | LIVE_CPU_REG_Y | LIVE_MEM; } progress = true; @@ -13859,7 +13860,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) #if 1 else if ( - mIns[i + 0].mType == ASMIT_CLC && + mIns[i + 0].mType == ASMIT_CLC && mIns[i + 1].mType == ASMIT_TYA && mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && mIns[i + 2].mAddress <= 2 && !(mIns[i + 2].mLive & (LIVE_CPU_REG_C | LIVE_CPU_REG_Y))) { @@ -13899,7 +13900,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) #endif else if ( mIns[i + 0].mType == ASMIT_ADC && mIns[i + 0].mMode == ASMIM_IMMEDIATE && - mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 1].mType == ASMIT_CLC && mIns[i + 2].mType == ASMIT_ADC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) { mIns[i + 0].mAddress += mIns[i + 2].mAddress; @@ -13909,7 +13910,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) } else if ( mIns[i + 0].mType == ASMIT_SBC && mIns[i + 0].mMode == ASMIM_IMMEDIATE && - mIns[i + 1].mType == ASMIT_SEC && + mIns[i + 1].mType == ASMIT_SEC && mIns[i + 2].mType == ASMIT_SBC && mIns[i + 2].mMode == ASMIM_IMMEDIATE && !(mIns[i + 2].mLive & LIVE_CPU_REG_C)) { mIns[i + 0].mAddress += mIns[i + 2].mAddress; @@ -13920,7 +13921,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) else if ( mIns[i + 0].mType == ASMIT_STA && !(mIns[i + 0].mFlags & NCIF_VOLATILE) && mIns[i + 2].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 2]) && - !mIns[i + 1].ChangesAddress() && !mIns[i + 1].ChangesGlobalMemory() && + !mIns[i + 1].ChangesAddress() && !mIns[i + 1].ChangesGlobalMemory() && !mIns[i + 1].ChangesYReg() && !mIns[i + 1].ChangesXReg()) { mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; @@ -13994,6 +13995,16 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) #endif #endif + else if ( + mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_ROL && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress != mIns[i + 0].mAddress && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && !(mIns[i + 2].mLive & (LIVE_MEM | LIVE_CPU_REG_C | LIVE_CPU_REG_Z))) + { + mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_ROL; mIns[i + 2].mAddress = mIns[i + 1].mAddress; mIns[i + 2].mLive |= LIVE_MEM; + mIns[i + 1].mType = ASMIT_ASL; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 1].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + progress = true; + } #if 1 else if ( mIns[i + 0].mType == ASMIT_ASL && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && @@ -14230,6 +14241,36 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) progress = true; } #endif +#if 1 + else if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && !mIns[i + 0].MayBeChangedOnAddress(mIns[i + 1]) && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && + mIns[i + 3].mType == ASMIT_ORA && mIns[i + 3].SameEffectiveAddress(mIns[i + 0])) + { + mIns[i + 2].mType = ASMIT_ORA; + mIns[i + 3].mType = ASMIT_NOP; mIns[i + 3].mMode = ASMIM_IMPLIED; + + progress = true; + } +#endif +#if 1 + else if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].mType == ASMIT_EOR && mIns[i + 1].mMode == ASMIM_IMMEDIATE && mIns[i + 1].mAddress == 0xff && + mIns[i + 2].mType == ASMIT_SEC && + mIns[i + 3].mType == ASMIT_ADC) + { + mIns.Insert(i + 4, mIns[i + 0]); + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 3].mType = ASMIT_LDA; mIns[i + 3].mLive |= LIVE_CPU_REG_C; + mIns[i + 4].mType = ASMIT_SBC; + + progress = true; + } +#endif #if 1 else if ( mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && diff --git a/oscar64/oscar64.cpp b/oscar64/oscar64.cpp index 3bab0ca..53c8fea 100644 --- a/oscar64/oscar64.cpp +++ b/oscar64/oscar64.cpp @@ -73,7 +73,7 @@ int main(int argc, const char** argv) #else strcpy(strProductName, "oscar64"); - strcpy(strProductVersion, "1.3.74"); + strcpy(strProductVersion, "1.4.75"); #ifdef __APPLE__ uint32_t length = sizeof(basePath); diff --git a/oscar64/oscar64.rc b/oscar64/oscar64.rc index 5a95c7f..7a5ea02 100644 --- a/oscar64/oscar64.rc +++ b/oscar64/oscar64.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,3,74,0 - PRODUCTVERSION 1,3,74,0 + FILEVERSION 1,4,75,0 + PRODUCTVERSION 1,4,75,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,12 +43,12 @@ BEGIN BEGIN VALUE "CompanyName", "oscar64" VALUE "FileDescription", "oscar64 compiler" - VALUE "FileVersion", "1.3.74.0" + VALUE "FileVersion", "1.4.75.0" VALUE "InternalName", "oscar64.exe" VALUE "LegalCopyright", "Copyright (C) 2021" VALUE "OriginalFilename", "oscar64.exe" VALUE "ProductName", "oscar64" - VALUE "ProductVersion", "1.3.74.0" + VALUE "ProductVersion", "1.4.75.0" END END BLOCK "VarFileInfo" diff --git a/oscar64setup/oscar64setup.vdproj b/oscar64setup/oscar64setup.vdproj index 350b56c..7ba101d 100644 --- a/oscar64setup/oscar64setup.vdproj +++ b/oscar64setup/oscar64setup.vdproj @@ -94,6 +94,12 @@ } "Entry" { + "MsmKey" = "8:_1ED10FB93DDA4801BF72003E21B2CE55" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_1F88FA4F35F043B3ABFCB552FCEA5CDD" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -1087,6 +1093,26 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1ED10FB93DDA4801BF72003E21B2CE55" + { + "SourcePath" = "8:..\\samples\\games\\missile.c" + "TargetName" = "8:missile.c" + "Tag" = "8:" + "Folder" = "8:_BC04C0DDE264410096618981E4E890DA" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_1F88FA4F35F043B3ABFCB552FCEA5CDD" { "SourcePath" = "8:..\\include\\c64\\rasterirq.c" @@ -3492,15 +3518,15 @@ { "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:oscar64" - "ProductCode" = "8:{109FC925-4A6D-4868-A60D-42BF3DFE541B}" - "PackageCode" = "8:{C6215A39-7DD0-4667-93FB-46373135B9C8}" + "ProductCode" = "8:{1FF1624F-A9FD-402D-806B-6CE9925FB690}" + "PackageCode" = "8:{47F7AA54-A5D7-4A82-8276-0DCD0FE332D4}" "UpgradeCode" = "8:{9AB61EFF-ACAC-4079-9950-8D96615CD4EF}" "AspNetVersion" = "8:2.0.50727.0" "RestartWWWService" = "11:FALSE" "RemovePreviousVersions" = "11:TRUE" "DetectNewerInstalledVersion" = "11:TRUE" "InstallAllUsers" = "11:FALSE" - "ProductVersion" = "8:1.3.74" + "ProductVersion" = "8:1.4.75" "Manufacturer" = "8:oscar64" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:" diff --git a/samples/games/build.sh b/samples/games/build.sh index 5b661af..38fcf47 100644 --- a/samples/games/build.sh +++ b/samples/games/build.sh @@ -2,4 +2,5 @@ ../../bin/oscar64 snake.c ../../bin/oscar64 lander.c -n ../../bin/oscar64 maze3d.c -n +../../bin/oscar64 missile.c -O3 -n diff --git a/samples/games/lander.c b/samples/games/lander.c index f0b8c3a..856440e 100644 --- a/samples/games/lander.c +++ b/samples/games/lander.c @@ -223,15 +223,15 @@ void game_loop(void) case GS_PLAYING: { // Check player input on every frame - joy_poll(1); - lander_move(&TheGame.lander, joyx[1], joyy[1]); + joy_poll(0); + lander_move(&TheGame.lander, joyx[0], joyy[0]); LanderCollision col = lander_check(&TheGame.lander); if (col == LCOL_GROUND) game_state(GS_COLLIDE); else if (col == LCOL_PAD) game_state(GS_LANDED); else - lander_show(&TheGame.lander, joyx[1], joyy[1]); + lander_show(&TheGame.lander, joyx[0], joyy[0]); } break; case GS_LANDED: if (!--TheGame.count) diff --git a/samples/games/make.bat b/samples/games/make.bat index 7d20b89..77ee061 100644 --- a/samples/games/make.bat +++ b/samples/games/make.bat @@ -1,4 +1,4 @@ call ..\..\bin\oscar64 snake.c call ..\..\bin\oscar64 -n lander.c call ..\..\bin\oscar64 -n maze3d.c -call ..\..\bin\oscar64 -n missile.c +call ..\..\bin\oscar64 -n -O3 missile.c diff --git a/samples/games/maze3d.c b/samples/games/maze3d.c index 91668c1..3c80026 100644 --- a/samples/games/maze3d.c +++ b/samples/games/maze3d.c @@ -419,19 +419,19 @@ int main(void) for(;;) { // Read joystick input - joy_poll(1); + joy_poll(0); // Forward or backward motion - if (joyy[1]) + if (joyy[0]) { // Target square - sbyte tx = px - dx * joyy[1]; - sbyte ty = py - dy * joyy[1]; + sbyte tx = px - dx * joyy[0]; + sbyte ty = py - dy * joyy[0]; // Check i empty if (maze[ty][tx] != '#') { - if (joyy[1] < 0) + if (joyy[0] < 0) { // Forward animation px = tx; @@ -457,7 +457,7 @@ int main(void) // Check if new rotation if (!rotate) { - if (joyx[1] == 1) + if (joyx[0] == 1) { // Rotate right sbyte t = dx; dx = -dy; dy = t; @@ -465,7 +465,7 @@ int main(void) maze_draw(zxdist0); screen_left(); } - else if (joyx[1] == -1) + else if (joyx[0] == -1) { // Rotate left sbyte t = dx; dx = dy; dy = -t; @@ -474,7 +474,7 @@ int main(void) screen_right(); } } - else if (!joyx[1]) + else if (!joyx[0]) { // No rotation, may rotate again in next frame rotate = false; diff --git a/samples/games/missile.c b/samples/games/missile.c index 6417963..c07e529 100644 --- a/samples/games/missile.c +++ b/samples/games/missile.c @@ -3,114 +3,295 @@ #include #include #include +#include #include #include #include +// Make some room #pragma region(main, 0x0a00, 0xc800, , , {code, data, bss, heap, stack} ) +// Sprite assets const char MissileSprites[] = { #embed "../resources/missilesprites.bin" }; +// Charset assets const char MissileChars[] = { #embed "../resources/missilechars.bin" }; +// Graphics areas in bank 3 #define Color1 ((char *)0xc800) #define Color2 ((char *)0xd800) #define Hires ((char *)0xe000) #define Sprites ((char *)0xd000) #define Charset ((char *)0xd800) +// Joystick and crosshair control int CrossX = 160, CrossY = 100; bool CrossP = false; char CrossDelay = 0; +// Display bitmap Bitmap sbm; const ClipRect scr = { 0, 0, 320, 200 }; +// Structure for a explosion struct Explosion { - int x, y; - char r; - Explosion * next; + int x, y; // Center of circle + char r; // Radius of circle + Explosion * next; // Next explosion in list }; +// Structure for a missile (defensive and ICBM) struct Missile { - int sx, sy, tx, ty, x, y, dx, dy; - int d; - char cnt; - Missile * next; + int sx, sy; // start position + int tx, ty; // target position + int x, y; // current position + int dx, dy; // distance in x and y + int d; // error term for Bresenham + sbyte stepx; // direction in x (+1 or -1) + sbyte cnt; // speed counter + Missile * next; // next missile in list }; +// Storage space for explosion Explosion explosions[16]; -Explosion * efree, * eused; +// First free and first used explosion +Explosion * efree, * eused; +// Storage space for defending missiles Missile missiles[8]; +// First free and first used missile Missile * mfree, * mused; +// Number of missiles available +char nmissiles; +// Storage space for ICMBs Missile icbms[16]; +// First free and first used ICBM Missile * ifree, * iused; +// Speed and number of ICBMs still incomming +char icbmspeed, icbmcount; -bool cities[6]; +// Cities not yet destroyed +bool cities[6]; +char ncities; +// Init status bar at top +void status_init(void) +{ + memset(Color1, ' ', 40); + memset(Color2, 1, 40); + for(char i=0; i<8; i++) + { + Color1[i + 2] = '0'; + Color1[i + 12] = '0'; + Color2[i + 12] = 7; + } +} + +// Expand an 8x8 charactor to 16x16 on screen +void char_put(char cx, char cy, char c, char color) +{ + // Get pointer to glyph data + const char * sp = MissileChars + 8 * c; + + // Loop over all pixel + for(char y=0; y<8; y++) + { + char cl = sp[y]; + for(char x=0; x<8; x++) + { + // Draw two pixel if bit is set + if (cl & 128) + { + bmmc_put(&sbm, cx + 2 * x, cy + 2 * y + 0, color); + bmmc_put(&sbm, cx + 2 * x, cy + 2 * y + 1, color); + } + + // Next bit + cl <<= 1; + } + } +} + +// Write a zero terminated string on screen +void char_write(char cx, char cy, const char * s, char color) +{ + // Loop over all characters + while (*s) + { + char_put(cx, cy, *s, color); + s++; + cx += 16; + } +} + +// Increment the score from a given digit on +void score_inc(char digit, unsigned val) +{ + // Lowest digit to increment + char at = 9 - digit; + + // Loop while there is still score to account for + while (val) + { + // Increment one character + char ch = Color1[at] + val % 10; + + // Remove low digit from number + val /= 10; + + // Check overflow + if (ch > '9') + { + ch -= 10; + val++; + } + + Color1[at] = ch; + + // Next higher character + at --; + } +} + +// Reset score and update high score +void score_reset(void) +{ + // Find first digit, where score and highscore differ + char i = 0; + while (i < 8 && Color1[i + 2] == Color1[i + 12]) + i++; + + // Check if new score is higher + if (i < 8 && Color1[i + 2] > Color1[i + 12]) + { + // If so, copy to highscore + while (i < 8) + { + Color1[i + 12] = Color1[i + 2]; + i++; + } + } + + // Clear score + for(char i=0; i<8; i++) + Color1[i + 2] = '0'; +} + +// Update number of missiles +void status_missiles(char num) +{ + char n = 0; + + // Draw full pairs + while (2 * n + 1 < num) + { + Color1[25 + n] = 92; + n++; + } + // Draw remaining single one + if (num & 1) + { + Color1[25 + n] = 93; + n++; + } + // Empty remainder + while (n < 15) + { + Color1[25 + n] = 94; + n++; + } +} + +// Initialize explosion list void explosion_init(void) { + // No explosion active eused = nullptr; + + // First free explosion element efree = explosions; + // Build list for(char i=0; i<15; i++) explosions[i].next = explosions + i + 1; + // Terminate last element explosions[15].next = nullptr; } +// Start a new explosion void explosion_start(int x, int y) { + // Free slot in list of explosions? if (efree) { + // Move entry from free to used list Explosion * e = efree; efree = e->next; e->next = eused; eused = e; + // Initialize position and size e->r = 0; e->x = x; e->y = y; } } +// Animate all explosions void explosion_animate(void) { + // Loop over active explosions with "e", use "ep" to point + // to previous explosion, so we can remove the current explosion + // from the list Explosion * e = eused, * ep = nullptr; while (e) { + // Remember next entry in list Explosion * en = e->next; + + // Increment phase (radius) e->r++; + + // Advance every fourth frame if (!(e->r & 3)) { + // Draw or erase outer perimeter depending on growing or + // shrinking explosion phase if (e->r <= 64) bmmc_circle(&sbm, &scr, e->x, e->y, e->r >> 2, 1); else bmmc_circle(&sbm, &scr, e->x, e->y, 33 - (e->r >> 2), 0); } + + // End of explosion live if (e->r == 128) { + // Remove explosion from used list if (ep) ep->next = e->next; else eused = e->next; + + // Prepend it to free list e->next = efree; efree = e; } else ep = e; + // Next explosion in list e = en; } } +// Initialize defending missile list void missile_init(void) { mused = nullptr; @@ -120,31 +301,47 @@ void missile_init(void) missiles[7].next = nullptr; } +// Add a new defending missile void missile_start(int sx, int sy, int tx, int ty) { - if (mfree) + // Check if entry in free list and missile in silo remaining + if (mfree && nmissiles > 0) { + // Detach from free list Missile * m = mfree; mfree = m->next; + + // Attach to active list m->next = mused; mused = m; + // Initialize start and target coordinates m->sx = sx >> 1; m->x = sx >> 1; m->sy = sy; m->y = sy; m->tx = tx >> 1; m->ty = ty; + // Initialize line drawing parameters m->dy = m->sy - m->ty; m->dx = m->tx - m->sx; + m->stepx = 1; if (m->dx < 0) + { m->dx = -m->dx; + m->stepx = -1; + } m->d = m->dy - m->dx; m->dx *= 2; m->dy *= 2; + + // Remove missile from silo + nmissiles--; + status_missiles(nmissiles); } } +// Animate all active missiles void missile_animate(void) { Missile * m = mused, * mp = nullptr; @@ -152,6 +349,7 @@ void missile_animate(void) { Missile * mn = m->next; + // Advance missile position using one step of Bresenham if (m->d >= 0) { m->y--; @@ -159,18 +357,18 @@ void missile_animate(void) } if (m->d < 0) { - if (m->tx < m->sx) - m->x--; - else - m->x++; + m->x += m->stepx; m->d += m->dy; } - if (bmmc_get(&sbm, m->x, m->y) == 1 || m->y == m->ty) + // Check if target reached + if (m->y == m->ty) { + // If so, clear line and start explosion bmmcu_line(&sbm, m->sx * 2, m->sy, m->tx * 2, m->ty, 0); explosion_start(m->x * 2, m->y); + // Remove from active list if (mp) mp->next = m->next; else @@ -180,6 +378,7 @@ void missile_animate(void) } else { + // Draw new pixel in missile trace bmmc_put(&sbm, m->x * 2, m->y, 3); mp = m; } @@ -188,6 +387,7 @@ void missile_animate(void) } } +// Initialize incomming ICBM list void icbm_init(void) { iused = nullptr; @@ -197,9 +397,10 @@ void icbm_init(void) icbms[15].next = nullptr; } +// Add a new ICBM to the attacking set void icbm_start(int sx, int sy, int tx, int ty) { - if (ifree) + if (icbmcount && ifree) { Missile * m = ifree; ifree = m->next; @@ -210,16 +411,22 @@ void icbm_start(int sx, int sy, int tx, int ty) m->sy = sy; m->y = sy; m->tx = tx >> 1; m->ty = ty; - m->cnt = 4; + m->cnt = 0; m->dy = m->ty - m->sy; m->dx = m->tx - m->sx; + m->stepx = 1; if (m->dx < 0) + { m->dx = -m->dx; + m->stepx = -1; + } m->d = m->dy - m->dx; m->dx *= 2; m->dy *= 2; + + icbmcount--; } } @@ -230,14 +437,16 @@ void icbm_animate(void) { Missile * mn = m->next; - m->cnt--; - - if (!m->cnt) + // Check speed of ICBMs + m->cnt += icbmspeed; + while (m->cnt > 0) { - bmmc_put(&sbm, m->x * 2, m->y, 2); + m->cnt -= 32; - m->cnt = 4; + // Draw pixel in trace + bmmc_put(&sbm, m->x * 2, m->y, 2); + // Advance using Bresenham if (m->d >= 0) { m->y++; @@ -245,80 +454,126 @@ void icbm_animate(void) } if (m->d < 0) { - if (m->tx < m->sx) - m->x--; - else - m->x++; + m->x += m->stepx; m->d += m->dy; } + // Check if colliding with cloud or target reached if (bmmc_get(&sbm, m->x * 2, m->y) == 1 || m->y == m->ty) { + // If so, clear trace and start explosion bmmcu_line(&sbm, m->sx * 2, m->sy, m->tx * 2, m->ty, 0); explosion_start(m->x * 2, m->y); + // Check if we hit the ground if (m->y == m->ty) { + // If so, find matching city int x = m->x * 2; char ix; if (x > 160) - ix = (x - 202) / 32 + 3; + ix = ((x - 202) >> 5) + 3; else - ix = (x - 58) / 32; - cities[ix] = false; - spr_show(ix + 1, false); + ix = (x - 58) >> 5; + + // Destroy, destroy, annihilate, kill, kill + if (cities[ix]) + { + ncities--; + cities[ix] = false; + spr_show(ix + 1, false); + } } + // Add score for destroyed ICBM + score_inc(0, 25); + // Remove from list if (mp) mp->next = m->next; else iused = m->next; m->next = ifree; ifree = m; - } - else - { - bmmc_put(&sbm, m->x * 2, m->y, 1); - mp = m; + m = nullptr; + + // End loop early + break; } } - else + + // If ICBM still in flight + if (m) + { + // Remember for list management mp = m; + // Draw white head + bmmc_put(&sbm, m->x * 2, m->y, 1); + } m = mn; } } -void mountains_init(void) +// Initialize game screen +void screen_init(void) { + // Clean up + bmmcu_rect_fill(&sbm, 0, 8, 320, 184, 0); + + // Draw bottom bmmcu_rect_fill(&sbm, 0, 192, 320, 8, 3); bmmc_quad_fill(&sbm, &scr, 0, 192, 16, 184, 32, 184, 48, 192, MixedColors[3][3]); bmmc_quad_fill(&sbm, &scr, 136, 192, 152, 184, 176, 184, 192, 192, MixedColors[3][3]); bmmc_quad_fill(&sbm, &scr, 272, 192, 288, 184, 304, 184, 320, 192, MixedColors[3][3]); + + // Show cities + for(char i=0; i<3; i++) + { + if (cities[i + 0]) + spr_set(i + 1, true, 70 + 32 * i, 222, 65, 15, false, false, false); + if (cities[i + 3]) + spr_set(i + 4, true, 214 + 32 * i, 222, 65, 15, false, false, false); + } } +// Clear inner area of screen +void screen_clear(void) +{ + bmmcu_rect_fill(&sbm, 0, 8, 320, 176, 0); +} + +// Interrupt routine for joystick control, called by raster IRQ at bottom +// of screen + __interrupt void joy_interrupt() { - joy_poll(1); + // Poll joystick + joy_poll(0); - CrossX += joyx[1]; CrossY += joyy[1]; + // Move crosshair coordinates + CrossX += 2 * joyx[0]; CrossY += 2 * joyy[0]; + // Stop at edges of screen if (CrossX < 8) CrossX = 8; else if (CrossX > 312) CrossX = 312; - if (CrossY < 16) - CrossY = 16; + if (CrossY < 20) + CrossY = 20; else if (CrossY > 172) CrossY = 172; + // Move crosshair sprite spr_move(0, CrossX + 14, CrossY + 40); - if (joyb[1]) + // Check button + if (joyb[0]) { + // Avoid quickfire and bouncing if (CrossDelay == 0) { + // Request fire from non interrupt code CrossP = true; CrossDelay = 4; } @@ -327,64 +582,267 @@ __interrupt void joy_interrupt() CrossDelay--; } +enum GameState +{ + GS_READY, // Getting ready + GS_LEVEL, // Show level message + GS_PLAYING, // Playing the game + GS_BONUS, // Landed on pad + GS_ARMAGEDDON, // Show end game animation + GS_END // Wait for restart +}; + +// State of the game +struct Game +{ + GameState state; + byte count, level; + +} TheGame; // Only one game, so global variable + +// Character buffer for some texts +char cbuffer[10]; + +void game_state(GameState state) +{ + + TheGame.state = state; + + switch(state) + { + case GS_READY: + // Start of new game + score_reset(); + screen_init(); + char_write(40, 100, s"READY PLAYER 1", 3); + + // New cities + for(char i=0; i<6; i++) + cities[i] = true; + ncities = 6; + TheGame.count = 60; + + // Starting at level 1 + TheGame.level = 0; + break; + + case GS_LEVEL: + // Advance to next level + TheGame.level++; + utoa(TheGame.level ,cbuffer, 10); + screen_clear(); + char_write(96, 100, s"LEVEL", 3); + char_write(96 + 16 * 6, 100, cbuffer, 3); + TheGame.count = 30; + break; + + case GS_PLAYING: + // Avoid old fire request + CrossP = false; + + // Setup display + screen_init(); + missile_init(); + explosion_init(); + icbm_init(); + + // A new set of 30 missiles + nmissiles = 30; + status_missiles(nmissiles); + + // Game parameters based on level + if (TheGame.level < 112) + icbmspeed = 8 + TheGame.level; + else + icbmspeed = 120; + + if (TheGame.level < 50) + icbmcount = 5 + TheGame.level / 2; + else + icbmcount = 30; + + TheGame.count = 15; + break; + + case GS_BONUS: + { + // Show bonus + + unsigned bonus = ncities * 50 + nmissiles * 5; + utoa(bonus, cbuffer, 10); + + char_write(96, 100, s"BONUS", 3); + char_write(96 + 16 * 6, 100, cbuffer, 3); + + score_inc(0, bonus); + TheGame.count = 30; + } break; + + case GS_ARMAGEDDON: + TheGame.count = 0; + break; + + case GS_END: + char_write(104, 92, s"THE END", 0); + TheGame.count = 120; + break; + } +} + +// Main game play code +void game_play(void) +{ + // Check if fire request + if (CrossP) + { + // Find lauch site + int sx = 160; + if (CrossX < 120) + sx = 24; + else if (CrossX > 200) + sx = 296 + + // Fire missile + missile_start(sx, 184, CrossX, CrossY); + + // Reset request + CrossP = false; + } + + // Wait for next ICMB to enter the game + if (!--TheGame.count) + { + // Pick target city + char ci; + do { + ci = rand() & 7; + } while (ci >= 6 || !cities[ci]); + + int cx; + if (ci < 3) + cx = 58 + 32 * ci; + else + cx = 202 + 32 * (ci - 3); + + // Launch ICBM + icbm_start((rand() & 0xff) + 32, 0, cx, 184); + + // Next lauch time + TheGame.count = 8 + (rand() & 63); + } + + // Advance defending missiles by four pixels + for(char i=0; i<4; i++) + missile_animate(); + + // Advance ICBMs + icbm_animate(); + + // Show explosions + explosion_animate(); +} + +// Main game loop, entered every VSYNC, slower if too busy with explosions +void game_loop(void) +{ + switch(TheGame.state) + { + case GS_READY: + if (!--TheGame.count) + game_state(GS_LEVEL); + break; + case GS_LEVEL: + if (!--TheGame.count) + game_state(GS_PLAYING); + break; + + case GS_PLAYING: + game_play(); + + // Check for level and game end coditions + if (!icbmcount && !iused && !eused) + game_state(GS_BONUS); + else if (ncities == 0) + game_state(GS_ARMAGEDDON); + break; + + case GS_BONUS: + if (!--TheGame.count) + game_state(GS_LEVEL); + break; + + case GS_ARMAGEDDON: + // Draw end game animation + TheGame.count++; + bmmc_circle(&sbm, &scr, 160, 100, TheGame.count, 1); + explosion_animate(); + if (TheGame.count == 90) + game_state(GS_END); + break; + + case GS_END: + if (!--TheGame.count) + game_state(GS_READY); + break; + } +} + +// Interrupts for status line and joystick routine RIRQCode bottom, top; int main(void) { + // Activate trampoline mmap_trampoline(); + // Disable CIA interrupts, we do not want interference + // with our joystick interrupt + cia_init(); + + // Copy assets mmap_set(MMAP_RAM); memcpy(Sprites, MissileSprites, 1024); memcpy(Charset, MissileChars, 2048); mmap_set(MMAP_NO_ROM); - vic_setmode(VICM_HIRES_MC, Color1, Hires); - spr_init(Color1); - - // initialize raster IRQ - rirq_init(true); - - vic.color_back = VCOL_BLACK; - vic.color_border = VCOL_BLACK; - + // Clean out screen space memset(Color1, 0x18, 1000); memset(Color2, 0x06, 1000); memset(Hires, 0, 8000); memset(Color2 + 40 * 23, 0x07, 80); - bm_init(&sbm, Hires, 40, 25); - missile_init(); - explosion_init(); - icbm_init(); - mountains_init(); + // initialize raster IRQ + rirq_init(true); + // Switch to hires mode + vic_setmode(VICM_HIRES_MC, Color1, Hires); + spr_init(Color1); + + // Black background and border + vic.color_back = VCOL_BLACK; + vic.color_border = VCOL_BLACK; + + // Init bitmap + bm_init(&sbm, Hires, 40, 25); + + // Init status line + status_init(); + + // Init cross hair sprite spr_set(0, true, CrossX + 14, CrossY + 40, 64, 1, false, false, false); - for(char i=0; i<3; i++) - { - spr_set(i + 1, true, 70 + 32 * i, 222, 65, 15, false, false, false); - spr_set(i + 4, true, 214 + 32 * i, 222, 65, 15, false, false, false); - - cities[i + 0] = true; - cities[i + 3] = true; - } - - // Build the switch to normal IRQ + // Build to multicolor highres at top of screen rirq_build(&top, 3); - // Change color for ceiling rirq_delay(&top, 10); rirq_write(&top, 1, &vic.ctrl1, VIC_CTRL1_BMM | VIC_CTRL1_DEN | VIC_CTRL1_RSEL | 3); rirq_write(&top, 2, &vic.memptr, 0x28); - // place this at the bottom rirq_set(0, 57, &top); - // Build the switch to normal IRQ + // Switch to text mode for status line and poll joystick at bottom rirq_build(&bottom, 3); rirq_write(&bottom, 0, &vic.memptr, 0x27); rirq_write(&bottom, 1, &vic.ctrl1, VIC_CTRL1_DEN | VIC_CTRL1_RSEL | 3); - // Change color for ceiling rirq_call(&bottom, 2, joy_interrupt); - // place this at the bottom rirq_set(1, 250, &bottom); // sort the raster IRQs @@ -393,42 +851,12 @@ int main(void) // start raster IRQ processing rirq_start(); - bool launched = false; + // start game state machine + game_state(GS_READY); for(;;) { - if (CrossP) - { - int sx = 160; - if (CrossX < 120) - sx = 24; - else if (CrossX > 200) - sx = 296 - missile_start(sx, 184, CrossX, CrossY); - CrossP = false; - } - - if (!(rand() & 63)) - { - char ci; - do { - ci = rand() & 7; - } while (ci >= 6 || !cities[ci]); - - int cx; - if (ci < 3) - cx = 58 + 32 * ci; - else - cx = 202 + 32 * (ci - 3); - - icbm_start((rand() & 0xff) + 32, 0, cx, 184); - } - - for(char i=0; i<4; i++) - missile_animate(); - icbm_animate(); - explosion_animate(); - - vic_waitFrame(); + game_loop(); + rirq_wait(); } return 0; diff --git a/samples/games/snake.c b/samples/games/snake.c index 07c9b29..88440e1 100644 --- a/samples/games/snake.c +++ b/samples/games/snake.c @@ -233,8 +233,8 @@ void game_loop(void) break; case GS_PLAYING: // Check player input on every frame - joy_poll(1); - snake_control(&TheGame.snake, joyx[1], joyy[1]); + joy_poll(0); + snake_control(&TheGame.snake, joyx[0], joyy[0]); if (!--TheGame.count) { diff --git a/samples/memmap/easyflash.crt b/samples/memmap/easyflash.crt index 01f49e74a17e5f706e8c4247d2208343a044a4f8..3b06a7949e6e8b94976206a1831217ddf1f69a6c 100644 GIT binary patch delta 217 zcmZ3`#J-@3eS!op0~GiNJ39tVyeK`J8qZhvd8v_Hw0|tf7jI93Bn*~@mF>g+= z=VWAL*qr46mSkLjB*_BSwz;bTEX9OHYSjU-6f>sO)>DjNDHcqr?XMgla;#Y7xEdjH HY$$R72@){D delta 221 zcmZ3`#J-@3eS(A_BLf7uCKgw^gv0vi(p!vY3{&5W%6(whZX zH!*Kcu;*lCWZInN0G4D#mSh2I+uYRvmSVypwdw#^iWyUC>nTRC6bq)*_E!!NIaVxk LT#XPpHWWDkjchWe diff --git a/samples/scrolling/cgrid8way.c b/samples/scrolling/cgrid8way.c index d51a5c5..2873284 100644 --- a/samples/scrolling/cgrid8way.c +++ b/samples/scrolling/cgrid8way.c @@ -388,8 +388,8 @@ int main(void) for(;;) { // Check the joystick - joy_poll(1); - if (joyx[1] == 1) + joy_poll(0); + if (joyx[0] == 1) { // Move to the right if (gridX < 24) @@ -399,7 +399,7 @@ int main(void) scroll_left(); } } - else if (joyx[1] == -1) + else if (joyx[0] == -1) { // Move to the left if (gridX > 0) @@ -409,7 +409,7 @@ int main(void) scroll_right(); } } - else if (joyy[1] == 1) + else if (joyy[0] == 1) { // Move down if (gridY < 39) @@ -419,7 +419,7 @@ int main(void) scroll_up(); } } - else if (joyy[1] == -1) + else if (joyy[0] == -1) { // Move up if (gridY > 0) diff --git a/samples/sprites/joycontrol.c b/samples/sprites/joycontrol.c index 05bda69..f04f0ed 100644 --- a/samples/sprites/joycontrol.c +++ b/samples/sprites/joycontrol.c @@ -33,11 +33,11 @@ int main(void) for(;;) { // Poll joytick - joy_poll(1); + joy_poll(0); // Change position according to joystick - spx += joyx[1]; - spy += joyy[1]; + spx += joyx[0]; + spy += joyy[0]; // Move sprites on screen spr_move(0, spx, spy);