From f25bf80a0f174157d2afa4d44054439a6b930ab6 Mon Sep 17 00:00:00 2001 From: drmortalwombat <90205530+drmortalwombat@users.noreply.github.com> Date: Tue, 25 Jan 2022 14:31:36 +0100 Subject: [PATCH] Fix LF line ending on windows, float and loop optimizations --- include/c64/joystick.c | 4 +- include/c64/joystick.h | 6 +- include/c64/rasterirq.c | 8 + include/c64/rasterirq.h | 3 + oscar64/InterCode.cpp | 79 ++++++-- oscar64/InterCode.h | 4 +- oscar64/NativeCodeGenerator.cpp | 70 +++++-- oscar64/Preprocessor.cpp | 4 +- oscar64/oscar64.cpp | 2 +- oscar64/oscar64.rc | 8 +- oscar64setup/oscar64setup.vdproj | 6 +- samples/games/lander.c | 279 +++++++++++++++++++++++++++ samples/games/make.bat | 2 + samples/games/snake.c | 282 ++++++++++++++++++++++++++++ samples/memmap/easyflash.crt | Bin 114976 -> 114976 bytes samples/resources/landersprites.bin | Bin 0 -> 192 bytes 16 files changed, 712 insertions(+), 45 deletions(-) create mode 100644 samples/games/lander.c create mode 100644 samples/games/make.bat create mode 100644 samples/games/snake.c create mode 100644 samples/resources/landersprites.bin diff --git a/include/c64/joystick.c b/include/c64/joystick.c index cea89aa..4fce85f 100644 --- a/include/c64/joystick.c +++ b/include/c64/joystick.c @@ -1,7 +1,7 @@ #include "joystick.h" -signed char joyx[2], joyy[2]; -bool joyb[2]; +sbyte joyx[2], joyy[2]; +bool joyb[2]; void joy_poll(char n) { diff --git a/include/c64/joystick.h b/include/c64/joystick.h index 7f3dc97..7e8cd12 100644 --- a/include/c64/joystick.h +++ b/include/c64/joystick.h @@ -1,8 +1,10 @@ #ifndef C64_JOYSTICK_H #define C64_JOYSTICK_H -extern signed char joyx[2], joyy[2]; -extern bool joyb[2]; +#include "types.h" + +extern sbyte joyx[2], joyy[2]; +extern bool joyb[2]; // poll joystick input for joystick 0 or 1 and place // the x/y direction and the button status into the joyx/y/b diff --git a/include/c64/rasterirq.c b/include/c64/rasterirq.c index ea6f1e6..5c1a351 100644 --- a/include/c64/rasterirq.c +++ b/include/c64/rasterirq.c @@ -245,6 +245,14 @@ void rirq_write(RIRQCode * ic, byte n, void * addr, byte data) ic->code[p] = data; } +void rirq_call(RIRQCode * ic, byte n, void * addr) +{ + byte p = irqai[n]; + ic->code[p - 1] = 0x20; + ic->code[p + 0] = (unsigned)addr & 0xff; + ic->code[p + 1] = (unsigned)addr >> 8; +} + void rirq_delay(RIRQCode * ic, byte cycles) { ic->code[ 1] = cycles; diff --git a/include/c64/rasterirq.h b/include/c64/rasterirq.h index 17762b6..8b61345 100644 --- a/include/c64/rasterirq.h +++ b/include/c64/rasterirq.h @@ -40,6 +40,9 @@ void rirq_build(RIRQCode * ic, byte size); // Add a write command to a raster IRQ inline void rirq_write(RIRQCode * ic, byte n, void * addr, byte data); +// Add a call command to a raster IRQ +inline void rirq_call(RIRQCode * ic, byte n, void * addr); + // Change the address of a raster IRQ write command inline void rirq_addr(RIRQCode * ic, byte n, void * addr); diff --git a/oscar64/InterCode.cpp b/oscar64/InterCode.cpp index 400648d..cada763 100644 --- a/oscar64/InterCode.cpp +++ b/oscar64/InterCode.cpp @@ -2597,6 +2597,24 @@ bool InterInstruction::ConstantFolding(void) mNumOperands = 1; return true; } + else if (mOperator == IA_MODU && (mSrc[0].mIntConst & (mSrc[0].mIntConst - 1)) == 0) + { + mOperator = IA_AND; + mSrc[0].mIntConst--; + return true; + } + else if (mOperator == IA_DIVU && (mSrc[0].mIntConst & (mSrc[0].mIntConst - 1)) == 0) + { + int n = 0; + while (mSrc[0].mIntConst > 1) + { + n++; + mSrc[0].mIntConst >>= 1; + } + mOperator = IA_SHR; + mSrc[0].mIntConst = n; + return true; + } } } else if (mSrc[1].mTemp < 0) @@ -3005,6 +3023,7 @@ void InterCodeBasicBlock::GenerateTraces(bool expand) else if (mTrueJump && !mFalseJump && ((expand && mTrueJump->mInstructions.Size() < 10 && mTrueJump->mInstructions.Size() > 1) || mTrueJump->mNumEntries == 1) && !mTrueJump->mLoopHead && !IsInfiniteLoop(mTrueJump, mTrueJump)) { mTrueJump->mNumEntries--; + int n = mTrueJump->mNumEntries; mInstructions.Pop(); for (i = 0; i < mTrueJump->mInstructions.Size(); i++) @@ -3013,10 +3032,13 @@ void InterCodeBasicBlock::GenerateTraces(bool expand) mFalseJump = mTrueJump->mFalseJump; mTrueJump = mTrueJump->mTrueJump; - if (mTrueJump) - mTrueJump->mNumEntries++; - if (mFalseJump) - mFalseJump->mNumEntries++; + if (n > 0) + { + if (mTrueJump) + mTrueJump->mNumEntries++; + if (mFalseJump) + mFalseJump->mNumEntries++; + } } else break; @@ -3474,6 +3496,11 @@ void InterCodeBasicBlock::CheckValueUsage(InterInstruction * ins, const GrowingI ins->mSrc[0].mIntConst = 3; } } + else if (ins->mOperator == IA_MODU && (ins->mSrc[0].mIntConst & (ins->mSrc[0].mIntConst - 1)) == 0) + { + ins->mOperator = IA_AND; + ins->mSrc[0].mIntConst--; + } } if (ins->mSrc[0].mTemp > 0 && ins->mSrc[1].mTemp > 0 && ins->mSrc[0].mTemp == ins->mSrc[1].mTemp) @@ -3915,6 +3942,17 @@ void InterCodeBasicBlock::SimplifyIntegerRangeRelops(void) { mInstructions[i]->mOperator = IA_EXT8TO16U; } + else if (mInstructions[i]->mCode == IC_BINARY_OPERATOR && mInstructions[i]->mOperator == IA_DIVS && mInstructions[i]->mSrc[0].IsUnsigned() && mInstructions[i]->mSrc[1].IsUnsigned()) + { + mInstructions[i]->mOperator = IA_DIVU; + mInstructions[i]->ConstantFolding(); + } + else if (mInstructions[i]->mCode == IC_BINARY_OPERATOR && mInstructions[i]->mOperator == IA_MODS && mInstructions[i]->mSrc[0].IsUnsigned() && mInstructions[i]->mSrc[1].IsUnsigned()) + { + mInstructions[i]->mOperator = IA_MODU; + mInstructions[i]->ConstantFolding(); + } + #endif } #endif @@ -6655,16 +6693,16 @@ void InterCodeBasicBlock::FollowJumps(void) } -InterCodeBasicBlock* InterCodeBasicBlock::PropagateDominator(InterCodeProcedure* proc) +InterCodeBasicBlock* InterCodeBasicBlock::BuildLoopPrefix(InterCodeProcedure* proc) { if (!mVisited) { mVisited = true; if (mTrueJump) - mTrueJump = mTrueJump->PropagateDominator(proc); + mTrueJump = mTrueJump->BuildLoopPrefix(proc); if (mFalseJump) - mFalseJump = mFalseJump->PropagateDominator(proc); + mFalseJump = mFalseJump->BuildLoopPrefix(proc); if (mLoopHead) { @@ -8458,7 +8496,7 @@ void InterCodeProcedure::Close(void) TempForwarding(); } - BuildDominators(); + BuildLoopPrefix(); DisassembleDebug("added dominators"); ResetVisited(); @@ -8655,6 +8693,21 @@ void InterCodeProcedure::Close(void) #endif +#if 1 + BuildLoopPrefix(); + DisassembleDebug("added dominators"); + + ResetVisited(); + mEntryBlock->SingleBlockLoopOptimisation(mParamAliasedSet); + + DisassembleDebug("single block loop opt 2"); + + BuildDataFlowSets(); + + BuildTraces(false); + DisassembleDebug("Rebuilt traces"); +#endif + #if 1 ResetVisited(); mEntryBlock->PeepholeOptimization(); @@ -8848,10 +8901,14 @@ void InterCodeProcedure::MergeBasicBlocks(void) } -void InterCodeProcedure::BuildDominators(void) +void InterCodeProcedure::BuildLoopPrefix(void) { ResetVisited(); - mEntryBlock = mEntryBlock->PropagateDominator(this); + + for (int i = 0; i < mBlocks.Size(); i++) + mBlocks[i]->mLoopPrefix = nullptr; + + mEntryBlock = mEntryBlock->BuildLoopPrefix(this); ResetVisited(); for (int i = 0; i < mBlocks.Size(); i++) @@ -9002,7 +9059,7 @@ void InterCodeProcedure::Disassemble(FILE* file) static char typechars[] = "NBCILFP"; for (int i = 0; i < mTemporaries.Size(); i++) { - fprintf(file, "$%02x T%d(%c), ", mTempOffset[i], i, typechars[mTemporaries[i]]); + fprintf(file, "$%02x R%d(%c), ", mTempOffset[i], i, typechars[mTemporaries[i]]); } fprintf(file, "\n"); diff --git a/oscar64/InterCode.h b/oscar64/InterCode.h index 42bc19d..9d985d6 100644 --- a/oscar64/InterCode.h +++ b/oscar64/InterCode.h @@ -445,7 +445,7 @@ public: void CollectLoopPath(const GrowingArray& body, GrowingArray& path); void InnerLoopOptimization(const NumberSet& aliasedParams); - InterCodeBasicBlock* PropagateDominator(InterCodeProcedure * proc); + InterCodeBasicBlock* BuildLoopPrefix(InterCodeProcedure * proc); void SplitBranches(InterCodeProcedure* proc); void FollowJumps(void); @@ -517,7 +517,7 @@ protected: void TempForwarding(void); void RemoveUnusedInstructions(void); bool GlobalConstantPropagation(void); - void BuildDominators(void); + void BuildLoopPrefix(void); void SingleAssignmentForwarding(void); void MergeBasicBlocks(void); diff --git a/oscar64/NativeCodeGenerator.cpp b/oscar64/NativeCodeGenerator.cpp index 2df76b7..b97b0e9 100644 --- a/oscar64/NativeCodeGenerator.cpp +++ b/oscar64/NativeCodeGenerator.cpp @@ -5106,6 +5106,7 @@ NativeCodeBasicBlock* NativeCodeBasicBlock::BinaryOperator(InterCodeProcedure* p { int sop0 = 0, sop1 = 1; bool flipop = false; + bool changedSign = false; if (ins->mOperator == IA_ADD || ins->mOperator == IA_MUL || ins->mOperator == IA_SUB) { @@ -5128,7 +5129,14 @@ NativeCodeBasicBlock* NativeCodeBasicBlock::BinaryOperator(InterCodeProcedure* p if (ins->mSrc[sop1].mTemp < 0) { union { float f; unsigned int v; } cc; - cc.f = ins->mSrc[sop1].mFloatConst; + + if (ins->mOperator == IA_SUB && flipop) + { + changedSign = true; + cc.f = -ins->mSrc[sop1].mFloatConst; + } + else + cc.f = ins->mSrc[sop1].mFloatConst; mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff)); mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 0)); @@ -5168,7 +5176,14 @@ NativeCodeBasicBlock* NativeCodeBasicBlock::BinaryOperator(InterCodeProcedure* p if (ins->mSrc[sop0].mTemp < 0) { union { float f; unsigned int v; } cc; - cc.f = ins->mSrc[sop0].mFloatConst; + + if (ins->mOperator == IA_SUB && !flipop) + { + changedSign = true; + cc.f = -ins->mSrc[sop0].mFloatConst; + } + else + cc.f = ins->mSrc[sop0].mFloatConst; mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff)); mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 0)); @@ -5214,17 +5229,20 @@ NativeCodeBasicBlock* NativeCodeBasicBlock::BinaryOperator(InterCodeProcedure* p } break; case IA_SUB: { - if (flipop) + if (!changedSign) { - mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); - mIns.Push(NativeCodeInstruction(ASMIT_EOR, ASMIM_IMMEDIATE, 0x80)); - mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); - } - else - { - mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK + 3)); - mIns.Push(NativeCodeInstruction(ASMIT_EOR, ASMIM_IMMEDIATE, 0x80)); - mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 3)); + if (flipop) + { + mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + mIns.Push(NativeCodeInstruction(ASMIT_EOR, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_ACCU + 3)); + } + else + { + mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_WORK + 3)); + mIns.Push(NativeCodeInstruction(ASMIT_EOR, ASMIM_IMMEDIATE, 0x80)); + mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 3)); + } } NativeCodeGenerator::Runtime& art(nproc->mGenerator->ResolveRuntime(Ident::Unique("faddsub"))); @@ -15103,17 +15121,33 @@ void NativeCodeProcedure::Compile(InterCodeProcedure* proc) mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_SBC, ASMIM_IMMEDIATE, commonFrameSize & 0xff)); mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK)); - mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); - mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_SBC, ASMIM_IMMEDIATE, (commonFrameSize >> 8) & 0xff)); - mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + if (commonFrameSize >= 256) + { + mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_SBC, ASMIM_IMMEDIATE, (commonFrameSize >> 8) & 0xff)); + mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } + else + { + mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_BCS, ASMIM_RELATIVE, 2)); + mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_DEC, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_CLC, ASMIM_IMPLIED)); mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK)); mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_ADC, ASMIM_IMMEDIATE, commonFrameSize & 0xff)); mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK)); - mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); - mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_ADC, ASMIM_IMMEDIATE, (commonFrameSize >> 8) & 0xff)); - mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + if (commonFrameSize >= 256) + { + mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_ADC, ASMIM_IMMEDIATE, (commonFrameSize >> 8) & 0xff)); + mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } + else + { + mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_BCC, ASMIM_RELATIVE, 2)); + mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_INC, ASMIM_ZERO_PAGE, BC_REG_STACK + 1)); + } } if (mNoFrame) diff --git a/oscar64/Preprocessor.cpp b/oscar64/Preprocessor.cpp index 5fb7bd0..b885afd 100644 --- a/oscar64/Preprocessor.cpp +++ b/oscar64/Preprocessor.cpp @@ -88,7 +88,7 @@ bool SourceFile::Open(const char* name, const char* path, bool binary) strcat_s(fname + n, sizeof(fname) - n, name); - if (!fopen_s(&mFile, fname, binary ? "rb" : "r")) + if (!fopen_s(&mFile, fname, "rb")) { _fullpath(mFileName, fname, sizeof(mFileName)); char* p = mFileName; @@ -158,7 +158,7 @@ bool Preprocessor::NextLine(void) mLocation.mLine++; s = strlen(mLine); - while (s > 0 && mLine[s - 1] == '\n') + while (s > 0 && (mLine[s - 1] == '\n' || mLine[s - 1] == '\r')) s--; if (s == 0 || mLine[s - 1] != '\\') return true; diff --git a/oscar64/oscar64.cpp b/oscar64/oscar64.cpp index 941339b..e59e5df 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.2.66"); + strcpy(strProductVersion, "1.2.67"); #ifdef __APPLE__ uint32_t length = sizeof(basePath); diff --git a/oscar64/oscar64.rc b/oscar64/oscar64.rc index 0256d34..c556bec 100644 --- a/oscar64/oscar64.rc +++ b/oscar64/oscar64.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,2,66,0 - PRODUCTVERSION 1,2,66,0 + FILEVERSION 1,2,67,0 + PRODUCTVERSION 1,2,67,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,12 +43,12 @@ BEGIN BEGIN VALUE "CompanyName", "oscar64" VALUE "FileDescription", "oscar64 compiler" - VALUE "FileVersion", "1.2.66.0" + VALUE "FileVersion", "1.2.67.0" VALUE "InternalName", "oscar64.exe" VALUE "LegalCopyright", "Copyright (C) 2021" VALUE "OriginalFilename", "oscar64.exe" VALUE "ProductName", "oscar64" - VALUE "ProductVersion", "1.2.66.0" + VALUE "ProductVersion", "1.2.67.0" END END BLOCK "VarFileInfo" diff --git a/oscar64setup/oscar64setup.vdproj b/oscar64setup/oscar64setup.vdproj index 9b85ef2..4812413 100644 --- a/oscar64setup/oscar64setup.vdproj +++ b/oscar64setup/oscar64setup.vdproj @@ -3169,15 +3169,15 @@ { "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:oscar64" - "ProductCode" = "8:{71F1F5C2-A7B5-46C1-8354-C7B870ADCD34}" - "PackageCode" = "8:{7F48C21E-E13E-479D-824A-5AB731A09EB1}" + "ProductCode" = "8:{118A6EF7-D160-4A3E-AECC-5B0D9AA16099}" + "PackageCode" = "8:{8B7C9187-A38D-43DB-B458-3E80FDB17616}" "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.2.66" + "ProductVersion" = "8:1.2.67" "Manufacturer" = "8:oscar64" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:" diff --git a/samples/games/lander.c b/samples/games/lander.c new file mode 100644 index 0000000..856440e --- /dev/null +++ b/samples/games/lander.c @@ -0,0 +1,279 @@ +#include +#include +#include +#include +#include + +byte landersprites[] = { +#embed "../resources/landersprites.bin" +}; + +// Screen and color ram address + +#define Screen ((byte *)0x0400) +#define Color ((byte *)0xd800) + +struct Lander +{ + float px, py, vx, vy; +}; + +enum GameState +{ + GS_READY, // Getting ready + GS_PLAYING, // Playing the game + GS_LANDED, // Landed on pad + GS_COLLIDE // Collided with something +}; + +// State of the game +struct Game +{ + GameState state; + byte count; + Lander lander; // use an array for multiplayer + +} TheGame; // Only one game, so global variable + +// Put one char on screen +inline void screen_put(byte x, byte y, char ch, char color) +{ + __assume(y < 25); + + Screen[40 * y + x] = ch; + Color[40 * y + x] = color; +} + +// Get one char from screen +inline char screen_get(byte x, byte y) +{ + __assume(y < 25); + + return Screen[40 * y + x]; +} + +void screen_init(void) +{ + // Fill screen with spaces + memset(Screen, ' ', 1000); + + for(char i=0; i<100; i++) + screen_put(rand() % 40, rand() % 25, '.', VCOL_WHITE) + + sbyte height[41]; + for(char i=0; i<41; i+=8) + height[i] = 4 + rand() % 16; + + for(char step = 8; step > 1; step /= 2) + { + for(char i=0; i<40; i+=step) + { + char p = (height[i] + height[i + step]) >> 1; + p += rand() % step - (step / 2); + height[i + step / 2] = p; + } + } + + char xp = 2 + rand() % 33; + char yp = height[xp]; + for(char i=1; i<4; i++) + if (height[xp + i] < yp) + yp = height[xp + i] + + for(char i=0; i<4; i++) + height[xp + i] = yp; + + for(char x=0; x<40; x++) + { + char h = height[x]; + for(char y=0; ypx = 160; + lander->py = 50; + lander->vx = 0; + lander->vy = 0; + + spr_set(0, true, (int)lander->px, (int)lander->py, 0x0380 / 64, VCOL_DARK_GREY, false, false, false); + spr_set(1, true, (int)lander->px, (int)lander->py, 0x0340 / 64, VCOL_LT_GREY, true, false, false); + spr_set(2, false, (int)lander->px, (int)lander->py + 20, 0x03c0 / 64, VCOL_WHITE, false, false, false); +} + +char ExhaustColor[] = {VCOL_YELLOW, VCOL_WHITE, VCOL_ORANGE, VCOL_LT_BLUE}; + +void lander_move(Lander * lander, sbyte jx, sbyte jy) +{ + lander->px += lander->vx; + lander->py += lander->vy; + lander->vx += jx * 0.02; + lander->vy += jy * 0.1 + 0.01; +} + +void lander_show(Lander * lander, sbyte jx, sbyte jy) +{ + vic.color_border++; + + int ix = (int)lander->px, iy = (int)lander->py; + + spr_move(0, ix, iy); + spr_move(1, ix, iy); + if (jy < 0) + { + spr_move(2, ix, iy + 20); + spr_color(2, ExhaustColor[rand() & 3]); + spr_show(2, true); + } + else + spr_show(2, false); + + vic.color_border--; +} + +void lander_flash(Lander * lander, char c) +{ + spr_color(0, rand() & 1); +} + +enum LanderCollision +{ + LCOL_FREE, + LCOL_GROUND, + LCOL_PAD +}; + +LanderCollision lander_check(Lander * lander) +{ + sbyte ix = (sbyte)((lander->px - 24) * 0.125); + sbyte iy = (sbyte)((lander->py - 29) * 0.125); + + if (iy > 24) + return LCOL_GROUND; + if (iy < 0) + return LCOL_FREE; + + LanderCollision col = LCOL_FREE; + for(char i=0; i<4; i++) + { + if (ix >= 0 && ix < 40) + { + char ch = screen_get(ix, iy); + if (ch == 160) + return LCOL_GROUND; + else if (ch == 128 + 86) + col = LCOL_PAD; + } + ix++; + } + + return col; +} + +void game_state(GameState state) +{ + // Set new state + TheGame.state = state; + + switch(state) + { + case GS_READY: + // Clear the screen + lander_init(&TheGame.lander) + screen_init(); + TheGame.count = 32; + break; + + case GS_PLAYING: + break; + + case GS_LANDED: + TheGame.lander.py = (sbyte)((TheGame.lander.py - 29) * 0.125) * 8 + 28; + lander_show(&TheGame.lander, 0, 0); + TheGame.count = 16; + break; + + case GS_COLLIDE: + TheGame.count = 32; + lander_show(&TheGame.lander, 0, 0); + break; + } +} + +// Main game loop, invoked every vsync +void game_loop(void) +{ + switch (TheGame.state) + { + case GS_READY: + // Countdown ready to start + if (!--TheGame.count) + game_state(GS_PLAYING); + break; + case GS_PLAYING: + { + // Check player input on every frame + 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[0], joyy[0]); + } break; + case GS_LANDED: + if (!--TheGame.count) + game_state(GS_READY); + break; + case GS_COLLIDE: + lander_flash(&TheGame.lander, TheGame.count); + if (!--TheGame.count) + game_state(GS_READY); + break; + } + +} + + + +int main(void) +{ + // Screen color to black + vic.color_border = VCOL_BLACK; + vic.color_back = VCOL_BLACK; + + vic.spr_mcolor0 = VCOL_MED_GREY; + vic.spr_mcolor1 = VCOL_YELLOW; + + memcpy((char *)0x0340, landersprites, 192); + + spr_init(Screen); + + // Start the game in ready state + game_state(GS_READY); + + // Forever + for(;;) + { + // One game loop iteration + game_loop(); + + // Wait one vsync + vic_waitFrame(); + } + + // Never reached + return 0; +} diff --git a/samples/games/make.bat b/samples/games/make.bat new file mode 100644 index 0000000..ca08359 --- /dev/null +++ b/samples/games/make.bat @@ -0,0 +1,2 @@ +call ..\..\bin\oscar64 snake.c +call ..\..\bin\oscar64 -n lander.c diff --git a/samples/games/snake.c b/samples/games/snake.c new file mode 100644 index 0000000..88440e1 --- /dev/null +++ b/samples/games/snake.c @@ -0,0 +1,282 @@ +#include +#include +#include +#include + + +// Position/Direction on screen +struct Point +{ + sbyte x, y; +}; + +struct Snake +{ + Point head; // Position of head + Point dir; // Direction of head + Point tail[256]; // Position of tail + byte length; // Length of tail + byte pos; // Tail start +}; + +enum GameState +{ + GS_READY, // Getting ready + GS_PLAYING, // Playing the game + GS_COLLIDE // Collided with something +}; + +// State of the game +struct Game +{ + GameState state; + byte count; + Snake snake; // use an array for multiplayer + +} TheGame; // Only one game, so global variable + + +// Screen and color ram address + +#define Screen ((byte *)0x0400) +#define Color ((byte *)0xd800) + +// Put one char on screen +inline void screen_put(byte x, byte y, char ch, char color) +{ + Screen[40 * y + x] = ch; + Color[40 * y + x] = color; +} + +// Get one char from screen +inline char screen_get(byte x, byte y) +{ + return Screen[40 * y + x]; +} + +// Put a fruit/heart at random position +void screen_fruit(void) +{ + byte x, y; + do + { + // Draw a random position + x = 1 + rand() % 38; + y = 1 + rand() % 23; + + // Ensure it is an empty place + } while (screen_get(x, y) != ' '); + + // Put the heart on screen + screen_put(x, y, 83, VCOL_YELLOW); + +} + +// Clear screen and draw borders +void screen_init(void) +{ + // Fill screen with spaces + memset(Screen, ' ', 1000); + + // Bottom and top row + for(byte x=0; x<40; x++) + { + screen_put(x, 0, 0xa0, VCOL_LT_GREY); + screen_put(x, 24, 0xa0, VCOL_LT_GREY); + } + + // Left and right column + for(byte y=0; y<25; y++) + { + screen_put( 0, y, 0xa0, VCOL_LT_GREY); + screen_put( 39, y, 0xa0, VCOL_LT_GREY); + } + +} + +// Initialize a snake +void snake_init(Snake * s) +{ + // Length of tail is one + s->length = 1; + s->pos = 0; + + // Snake in the center of screen + s->head.x = 20; + s->head.y = 12; + + // Starting to the right + s->dir.x = 1; + s->dir.y = 0; + + // Show head + screen_put(s->head.x, s->head.y, 81, VCOL_WHITE); +} + +bool snake_advance(Snake * s) +{ + // Promote head to start of tail + s->tail[s->pos] = s->head; + s->pos++; + + screen_put(s->head.x, s->head.y, 81, VCOL_LT_BLUE); + + // Advance head + s->head.x += s->dir.x; + s->head.y += s->dir.y; + + // Get character at new head position + char ch = screen_get(s->head.x, s->head.y); + + // Draw head + screen_put(s->head.x, s->head.y, 81, VCOL_WHITE); + + // Clear tail + char tpos = s->pos - s->length; + screen_put(s->tail[tpos].x, s->tail[tpos].y, ' ', VCOL_BLACK); + + // Did snake collect the fruit + if (ch == 83) + { + // Extend tail + s->length++; + screen_fruit(); + } + else if (ch != ' ') + { + // Snake collided with something + return true; + } + + return false; +} + +// flash the snake after collision +bool snake_flash(Snake * s, char c) +{ + // Loop over all tail elements + for(char i=0; ilength; i++) + { + // Set color + char tpos = s->pos - i - 1; + screen_put(s->tail[tpos].x, s->tail[tpos].y, 81, c); + } +} + +// Change snake direction based on user input +void snake_control(Snake * s, sbyte jx, sbyte jy) +{ + // First change from horizontal to vertical, otherwise + // check vertical to horizontal + if (s->dir.x && jy) + { + s->dir.x = 0; + s->dir.y = jy; + } + else if (s->dir.y && jx) + { + s->dir.y = 0; + s->dir.x = jx; + } +} + +void game_state(GameState state) +{ + // Set new state + TheGame.state = state; + + switch(state) + { + case GS_READY: + // Clear the screen + screen_init(); + TheGame.count = 32; + break; + + case GS_PLAYING: + // Init the snake + snake_init(&TheGame.snake); + + // Initial fruit + screen_fruit(); + + TheGame.count = 16; + break; + + case GS_COLLIDE: + TheGame.count = 16; + break; + } +} + +// Colors for collision "animation" +char FlashColors[] = { + VCOL_YELLOW, + VCOL_WHITE, + VCOL_LT_GREY, + VCOL_YELLOW, + VCOL_ORANGE, + VCOL_RED, + VCOL_MED_GREY, + VCOL_DARK_GREY +}; + +// Main game loop, invoked every vsync +void game_loop(void) +{ + switch (TheGame.state) + { + case GS_READY: + // Countdown ready to start + if (!--TheGame.count) + game_state(GS_PLAYING); + break; + case GS_PLAYING: + // Check player input on every frame + joy_poll(0); + snake_control(&TheGame.snake, joyx[0], joyy[0]); + + if (!--TheGame.count) + { + // Move snake every four frames, advance to collision + // state if collided + if (snake_advance(&TheGame.snake)) + game_state(GS_COLLIDE); + else + TheGame.count = 4; + } + break; + case GS_COLLIDE: + // Flash the collided snake + snake_flash(&TheGame.snake, FlashColors[(16 - TheGame.count) / 2]); + if (!--TheGame.count) + game_state(GS_READY); + break; + } + +} + + + +int main(void) +{ + // Screen color to black + vic.color_border = VCOL_BLACK; + vic.color_back = VCOL_BLACK; + + // Start the game in ready state + game_state(GS_READY); + + // Forever + for(;;) + { + // One game loop iteration + game_loop(); + + // Wait one vsync + vic_waitFrame(); + } + + // Never reached + return 0; +} diff --git a/samples/memmap/easyflash.crt b/samples/memmap/easyflash.crt index 7e421e41780698e8747fcdc8bb1637204ab0fdaa..0cfff62a09cd6a6d3486806d7093953cefa1f77e 100644 GIT binary patch delta 520 zcma*jKS%;m9Ki9rcZZ@r|CA7F1Xs^NT7qbh2sCVHa5z1$)|Q3?9h)=b2S$sJ8)}LN z^$uQ>hl4l-K_Ewy4UK^iT(~tw&`>zE-}0S4)0cI^tP}2?uQ`wv7;;5{XZSODFC-?> ztT*}bbI+5nqUg^Y%0I9Jae*PB=~3YfuH|(()Wm=uTDFI_l|CBVVP)lX8qIKGg@E*s zpFb*cGE;%{(KyePks(piO??XqbQW?6n4U}EpzE4Y^IQ|^^BD6qxXzRpvtQyhi1Rum z7;;TWvxrMzvFj38;07dl15(;a%-1u#W)+~;Lw-G;;f7TJgEuUxx?~TPL;2NCs`*G0 zXO5GAMlE|sEhTR7U4`!RG~M$n)tG&uZphYPq+Tu4X=I>lo7$?w5G4Y8l!)>?ZI2S| kGA8~fYz=P9zoh`|sU3*DKL%k#MV13OvK!1*&g8e!A3?IvivR!s delta 522 zcma*jze@sP7zc3gN1FPol?`gk*?WUDRMa9toMA(QgLkL(7dYG+=on@^;poc+HNH?U z@j7%KH-tkFv>Z+L2h>trxHYvj6b|jTJbZ@_U%ouF7o)w{;B3`_JV#I{b2LR?nIVtR z2+8}CpP=wO`7ZPE)FJE>IgsKA(n>wS9q&q56QXSd;zP&w#hp#2P9valfzA=kl|I3A z-o;cTn8sa9C4!ZYM2=xA3Ye)@1nhN5Y3b1PT?_^s_FWxJ-_@bvyZUezjB}mUsr1qu zY0QKaYe1TcZWFSzLRjj%7#5ih8D_wOa*_<{Ic8cVFb87L$T8h2fj$reqgJ%>AhjdHCFh~tjEEo${WJ5H diff --git a/samples/resources/landersprites.bin b/samples/resources/landersprites.bin new file mode 100644 index 0000000000000000000000000000000000000000..df258962e2ed637d421f48ab7c7e3a97889b62c8 GIT binary patch literal 192 zcmY++tqy`v0EXfB@T2@R2OC9q!U&>qB7o*<|Mtu-jDzX&diqRHcTX{%F$ymiwITGguHxhVaM!kKi zY+Y^jBh*c(n_Y6Ykd{%c=TU^kila;E&%;LTl~%D1;yieB!JK<*l5Sm!x>!sFGfa@P Mq!WmK_@4iX5AlaC1^@s6 literal 0 HcmV?d00001