From fdb112a037fb787203fd222248a40fecb91a8e3c Mon Sep 17 00:00:00 2001 From: drmortalwombat <90205530+drmortalwombat@users.noreply.github.com> Date: Mon, 14 Feb 2022 20:13:30 +0100 Subject: [PATCH] Add constant address calculation for struct members --- autotest/autotest.bat | 3 + autotest/ptrinittest.c | 37 ++ include/c64/keyboard.c | 6 +- include/c64/keyboard.h | 5 + oscar64/Declaration.cpp | 94 ++- oscar64/Declaration.h | 1 + oscar64/InterCode.cpp | 111 +++- oscar64/InterCodeGenerator.cpp | 23 +- oscar64/NativeCodeGenerator.cpp | 169 ++++-- oscar64/Parser.cpp | 8 +- oscar64/oscar64.cpp | 2 +- oscar64/oscar64.rc | 8 +- oscar64setup/oscar64setup.vdproj | 84 ++- samples/games/build.sh | 1 + samples/games/connectfour.c | 819 ++++++++++++++++++++++++++ samples/games/make.bat | 1 + samples/memmap/easyflash.crt | Bin 114976 -> 114976 bytes samples/resources/connect4chars.bin | Bin 0 -> 2048 bytes samples/resources/connect4sprites.bin | Bin 0 -> 1024 bytes 19 files changed, 1314 insertions(+), 58 deletions(-) create mode 100644 autotest/ptrinittest.c create mode 100644 samples/games/connectfour.c create mode 100644 samples/resources/connect4chars.bin create mode 100644 samples/resources/connect4sprites.bin diff --git a/autotest/autotest.bat b/autotest/autotest.bat index 42be630..e0f05fd 100644 --- a/autotest/autotest.bat +++ b/autotest/autotest.bat @@ -126,6 +126,9 @@ rem @echo off @call :test charwintest.c @if %errorlevel% neq 0 goto :error +@call :test ptrinittest.c +@if %errorlevel% neq 0 goto :error + @exit /b 0 :error diff --git a/autotest/ptrinittest.c b/autotest/ptrinittest.c new file mode 100644 index 0000000..7937b6a --- /dev/null +++ b/autotest/ptrinittest.c @@ -0,0 +1,37 @@ +#if 1 + +const struct A { + char w; + int b[5]; + struct { + int c[5]; + } o; +} a = {22, + {4, 5, 6, 7, 8}, + { + {4, 5, 6, 7, 8} + } +} + +const int * t[4] = { + a.b + 1 + 1 +}; + +const int * v[4] = { + a.o.c + 1 + 1 +}; + +int q[5] = {4, 5, 6, 7, 8}; + +const int * u[4] = { + q + 2 +}; + +int main(void) +{ + return + u[0][0] + (q + 2)[2] - 6 - 8 + + v[0][0] + (a.o.c + 2)[2] - 6 - 8 + + t[0][0] + (a.b + 2)[2] - 6 - 8; +} + diff --git a/include/c64/keyboard.c b/include/c64/keyboard.c index 72e8349..b51c576 100644 --- a/include/c64/keyboard.c +++ b/include/c64/keyboard.c @@ -8,8 +8,8 @@ const char keyb_codes[128] = { '7', 'y', 'g', '8', 'b', 'h', 'u', 'v', '9', 'i', 'j', '0', 'm', 'k', 'o', 'n', '+', 'p', 'l', '-', '.', ':', '@', ',', - 0 , '*', ';', 0, 0, '=', '^', '/', - '1', 0, 0, '2', ' ', 0, 'q', KEY_ESC, + 0 , '*', ';', KEY_HOME, 0, '=', '^', '/', + '1', KEY_ARROW_LEFT, 0, '2', ' ', 0, 'q', KEY_ESC, KEY_INST, KEY_RETURN, KEY_CSR_LEFT, KEY_F8, KEY_F2, KEY_F4, KEY_F6, KEY_CSR_UP, '#', 'W', 'A', '$', 'Z', 'S', 'E', 0, @@ -17,7 +17,7 @@ const char keyb_codes[128] = { '\'', 'Y', 'G', '(', 'B', 'H', 'U', 'V', ')', 'I', 'J', '0', 'M', 'K', 'O', 'N', 0, 'P', 'L', 0, '>', '[', '@', '<', - 0, 0, ']', 0, 0, 0, '^', '?', + 0, 0, ']', KEY_CLR, 0, 0, '^', '?', '!', 0, 0, '"', ' ', 0, 'Q', KEY_ESC, }; diff --git a/include/c64/keyboard.h b/include/c64/keyboard.h index 70ef6b0..d00981a 100644 --- a/include/c64/keyboard.h +++ b/include/c64/keyboard.h @@ -8,11 +8,16 @@ #define KEY_CSR_UP (17 + 128) #define KEY_CSR_LEFT (29 + 128) +#define KEY_ARROW_LEFT (95) + #define KEY_ESC (27) #define KEY_DEL (20) #define KEY_INST (148) #define KEY_RETURN (13) +#define KEY_HOME (19) +#define KEY_CLR (147) + #define KEY_F1 (133) #define KEY_F3 (134) #define KEY_F5 (135) diff --git a/oscar64/Declaration.cpp b/oscar64/Declaration.cpp index db105cb..7a1bb43 100644 --- a/oscar64/Declaration.cpp +++ b/oscar64/Declaration.cpp @@ -388,13 +388,103 @@ Expression* Expression::ConstantFold(Errors * errors) ex->mDecType = dec->mBase; return ex; } -} +#if 0 + else if (mLeft->mDecValue->mType == DT_CONST_POINTER && mRight->mDecValue->mType == DT_CONST_INTEGER && (mToken == TK_ADD || mToken == TK_SUB)) + { + int64 ileft = 0; + + Declaration* pdec = mLeft->mDecValue->mValue->mDecValue; + if (pdec->mType == DT_VARIABLE_REF) + { + ileft = pdec->mOffset; + pdec = pdec->mBase; + } + + switch (mToken) + { + case TK_ADD: + ileft += mRight->mDecValue->mInteger * mLeft->mDecType->mBase->mSize; + break; + case TK_SUB: + ileft -= mRight->mDecValue->mInteger * mLeft->mDecType->mBase->mSize; + break; + } + + Expression* vex = new Expression(mLocation, EX_VARIABLE); + Declaration* vdec = new Declaration(mLocation, DT_VARIABLE_REF); + vdec->mFlags = pdec->mFlags; + vdec->mBase = pdec; + vdec->mSize = pdec->mBase->mSize; + vdec->mOffset = ileft; + vex->mDecValue = vdec; + vex->mDecType = mLeft->mDecType->mBase; + + Expression* ex = new Expression(mLocation, EX_CONSTANT); + Declaration* dec = new Declaration(mLocation, DT_CONST_POINTER); + dec->mBase = mLeft->mDecValue->mBase; + dec->mValue = vex; + ex->mDecValue = dec; + ex->mDecType = dec->mBase; + return ex; + } +#endif + } + else if (mType == EX_BINARY && mToken == TK_ADD && mLeft->mType == EX_VARIABLE && mLeft->mDecValue->mType == DT_VARIABLE && (mLeft->mDecValue->mFlags & DTF_GLOBAL) && mLeft->mDecType->mType == DT_TYPE_ARRAY && mRight->mType == EX_CONSTANT && mRight->mDecValue->mType == DT_CONST_INTEGER) + { + Expression* ex = new Expression(mLocation, EX_VARIABLE); + Declaration* dec = new Declaration(mLocation, DT_VARIABLE_REF); + dec->mFlags = mLeft->mDecValue->mFlags; + dec->mBase = mLeft->mDecValue; + dec->mSize = mLeft->mDecType->mBase->mSize - mRight->mDecValue->mInteger * dec->mSize; + dec->mOffset = mRight->mDecValue->mInteger * dec->mSize; + ex->mDecValue = dec; + ex->mDecType = mLeft->mDecType; + return ex; + } + else if (mType == EX_BINARY && mToken == TK_ADD && mLeft->mType == EX_VARIABLE && mLeft->mDecValue->mType == DT_VARIABLE_REF && (mLeft->mDecValue->mFlags & DTF_GLOBAL) && mLeft->mDecType->mType == DT_TYPE_ARRAY && mRight->mType == EX_CONSTANT && mRight->mDecValue->mType == DT_CONST_INTEGER) + { + Expression* ex = new Expression(mLocation, EX_VARIABLE); + Declaration* dec = new Declaration(mLocation, DT_VARIABLE_REF); + dec->mFlags = mLeft->mDecValue->mFlags; + dec->mBase = mLeft->mDecValue->mBase; + dec->mSize = mLeft->mDecType->mBase->mSize - mRight->mDecValue->mInteger * dec->mSize; + dec->mOffset = mLeft->mDecValue->mOffset + mRight->mDecValue->mInteger * dec->mSize; + ex->mDecValue = dec; + ex->mDecType = mLeft->mDecType; + return ex; + } + else if (mType == EX_QUALIFY && mLeft->mType == EX_VARIABLE && mLeft->mDecValue->mType == DT_VARIABLE && (mLeft->mDecValue->mFlags & DTF_GLOBAL) && mLeft->mDecType->mType == DT_TYPE_STRUCT) + { + Expression* ex = new Expression(mLocation, EX_VARIABLE); + Declaration* dec = new Declaration(mLocation, DT_VARIABLE_REF); + dec->mFlags = mLeft->mDecValue->mFlags; + dec->mBase = mLeft->mDecValue; + dec->mOffset = mDecValue->mOffset; + dec->mSize = mDecValue->mSize; + ex->mDecValue = dec; + ex->mDecType = mDecType; + return ex; + } + else if (mType == EX_QUALIFY && mLeft->mType == EX_VARIABLE && mLeft->mDecValue->mType == DT_VARIABLE_REF && (mLeft->mDecValue->mFlags & DTF_GLOBAL) && mLeft->mDecType->mType == DT_TYPE_STRUCT) + { + Expression* ex = new Expression(mLocation, EX_VARIABLE); + Declaration* dec = new Declaration(mLocation, DT_VARIABLE_REF); + dec->mFlags = mLeft->mDecValue->mFlags; + dec->mBase = mLeft->mDecValue->mBase; + dec->mOffset = mLeft->mDecValue->mOffset + mDecValue->mOffset; + dec->mSize = mDecValue->mSize; + ex->mDecValue = dec; + ex->mDecType = mDecType; + return ex; + } return this; } Declaration::Declaration(const Location& loc, DecType type) - : mLocation(loc), mType(type), mScope(nullptr), mData(nullptr), mIdent(nullptr), mSize(0), mOffset(0), mFlags(0), mComplexity(0), mLocalSize(0), mBase(nullptr), mParams(nullptr), mValue(nullptr), mNext(nullptr), mVarIndex(-1), mLinkerObject(nullptr), mCallers(nullptr), mCalled(nullptr), mAlignment(1) + : mLocation(loc), mType(type), mScope(nullptr), mData(nullptr), mIdent(nullptr), mSize(0), mOffset(0), mFlags(0), mComplexity(0), mLocalSize(0), + mBase(nullptr), mParams(nullptr), mValue(nullptr), mNext(nullptr), mVarIndex(-1), mLinkerObject(nullptr), mCallers(nullptr), mCalled(nullptr), mAlignment(1), + mInteger(0), mNumber(0) {} Declaration::~Declaration(void) diff --git a/oscar64/Declaration.h b/oscar64/Declaration.h index 26c8c59..63c139f 100644 --- a/oscar64/Declaration.h +++ b/oscar64/Declaration.h @@ -34,6 +34,7 @@ enum DecType DT_CONST_DATA, DT_CONST_STRUCT, DT_CONST_POINTER, + DT_CONST_REFERENCE, DT_CONST_ASSEMBLER, DT_VARIABLE, diff --git a/oscar64/InterCode.cpp b/oscar64/InterCode.cpp index 437c643..b5aab9d 100644 --- a/oscar64/InterCode.cpp +++ b/oscar64/InterCode.cpp @@ -287,7 +287,10 @@ static bool MemRange(const InterInstruction* ins, const GrowingInstructionPtrArr } else if (ins->mSrc[1].mMemory == IM_INDIRECT) { - size = ins->mSrc[1].mOperandSize; + if (ins->mCode == IC_COPY) + size = ins->mConst.mOperandSize; + else + size = ins->mSrc[1].mOperandSize; return MemPtrRange(tvalue[ins->mSrc[1].mTemp], tvalue, mem, vindex, offset); } @@ -6452,7 +6455,7 @@ bool InterCodeBasicBlock::IsLeafProcedure(void) static bool CanBypassLoad(const InterInstruction * lins, const InterInstruction * bins) { // Check ambiguity - if (bins->mCode == IC_STORE || bins->mCode == IC_COPY || bins->mCode == IC_STRCPY) + if (bins->mCode == IC_COPY || bins->mCode == IC_STRCPY) return false; // Side effects @@ -6463,6 +6466,36 @@ static bool CanBypassLoad(const InterInstruction * lins, const InterInstruction if (bins->UsesTemp(lins->mDst.mTemp)) return false; + if (bins->mCode == IC_STORE) + { + if (lins->mVolatile) + return false; + else if (lins->mSrc[0].mTemp >= 0 || bins->mSrc[1].mTemp >= 0) + return false; + else if (lins->mSrc[0].mMemory != bins->mSrc[1].mMemory) + return true; + else if (lins->mSrc[0].mMemory == IM_GLOBAL) + { + return lins->mSrc[0].mLinkerObject != bins->mSrc[1].mLinkerObject || + lins->mSrc[0].mIntConst + lins->mSrc[0].mOperandSize <= bins->mSrc[1].mIntConst || + lins->mSrc[0].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else if (lins->mSrc[0].mMemory == IM_ABSOLUTE) + { + return + lins->mSrc[0].mIntConst + lins->mSrc[0].mOperandSize <= bins->mSrc[1].mIntConst || + lins->mSrc[0].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else if (lins->mSrc[0].mMemory == IM_LOCAL) + { + return lins->mSrc[0].mVarIndex != bins->mSrc[1].mVarIndex || + lins->mSrc[0].mIntConst + lins->mSrc[0].mOperandSize <= bins->mSrc[1].mIntConst || + lins->mSrc[0].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else + return false; + } + // False data dependency if (lins->mSrc[0].mTemp >= 0 && lins->mSrc[0].mTemp == bins->mDst.mTemp) return false; @@ -6522,6 +6555,58 @@ static bool CanBypassUp(const InterInstruction* lins, const InterInstruction* bi return true; } +static bool CanBypassLoadUp(const InterInstruction* lins, const InterInstruction* bins) +{ + // Check ambiguity + if (bins->mCode == IC_COPY || bins->mCode == IC_STRCPY) + return false; + + // Side effects + if (bins->mCode == IC_CALL || bins->mCode == IC_CALL_NATIVE || bins->mCode == IC_ASSEMBLER) + return false; + + // False data dependency + if (bins->UsesTemp(lins->mDst.mTemp) || bins->mDst.mTemp == lins->mDst.mTemp) + return false; + + // True data dependency + if (lins->mSrc[0].mTemp >= 0 && lins->mSrc[0].mTemp == bins->mDst.mTemp) + return false; + + if (bins->mCode == IC_STORE) + { + if (lins->mVolatile) + return false; + else if (lins->mSrc[0].mTemp >= 0 || bins->mSrc[1].mTemp >= 0) + return false; + else if (lins->mSrc[0].mMemory != bins->mSrc[1].mMemory) + return true; + else if (lins->mSrc[0].mMemory == IM_GLOBAL) + { + return lins->mSrc[0].mLinkerObject != bins->mSrc[1].mLinkerObject || + lins->mSrc[0].mIntConst + lins->mSrc[0].mOperandSize <= bins->mSrc[1].mIntConst || + lins->mSrc[0].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else if (lins->mSrc[0].mMemory == IM_ABSOLUTE) + { + return + lins->mSrc[0].mIntConst + lins->mSrc[0].mOperandSize <= bins->mSrc[1].mIntConst || + lins->mSrc[0].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else if (lins->mSrc[0].mMemory == IM_LOCAL) + { + return lins->mSrc[0].mVarIndex != bins->mSrc[1].mVarIndex || + lins->mSrc[0].mIntConst + lins->mSrc[0].mOperandSize <= bins->mSrc[1].mIntConst || + lins->mSrc[0].mIntConst >= bins->mSrc[1].mIntConst + bins->mSrc[1].mOperandSize; + } + else + return false; + } + + return true; +} + + static bool IsChained(const InterInstruction* ins, const InterInstruction* nins) { if (ins->mDst.mTemp >= 0) @@ -6562,7 +6647,7 @@ static bool CanBypassStore(const InterInstruction * sins, const InterInstruction { bm = bins->mSrc[0].mMemory; bi = bins->mSrc[0].mVarIndex; - st = sins->mSrc[0].mTemp; + bt = sins->mSrc[0].mTemp; bo = sins->mSrc[0].mIntConst; bz = InterTypeSize[sins->mDst.mType]; } @@ -6572,7 +6657,7 @@ static bool CanBypassStore(const InterInstruction * sins, const InterInstruction bi = bins->mSrc[1].mVarIndex; bt = sins->mSrc[1].mTemp; bo = sins->mSrc[1].mIntConst; - bz = InterTypeSize[sins->mSrc[1].mType]; + bz = InterTypeSize[sins->mSrc[0].mType]; } // Check ambiguity @@ -7816,6 +7901,7 @@ void InterCodeBasicBlock::PeepholeOptimization(void) i--; } #endif +#if 1 i = 0; while (i < mInstructions.Size()) { @@ -7844,10 +7930,24 @@ void InterCodeBasicBlock::PeepholeOptimization(void) if (i != j) mInstructions[j] = ins; } + else if (mInstructions[i]->mCode == IC_LOAD && mInstructions[i]->mSrc[0].mMemory == IM_INDIRECT && mInstructions[i]->mSrc[0].mFinal && mInstructions[i]->mDst.mType == IT_INT8) + { + InterInstruction* ins(mInstructions[i]); + int j = i; + while (j > 0 && CanBypassLoadUp(ins, mInstructions[j - 1])) + { + mInstructions[j] = mInstructions[j - 1]; + j--; + } + if (i != j) + mInstructions[j] = ins; + } i++; } +#endif +#if 1 i = limit; while (i >= 0) { @@ -7864,7 +7964,7 @@ void InterCodeBasicBlock::PeepholeOptimization(void) if (i != j) mInstructions[j] = ins; } - else if (mInstructions[i]->mCode == IC_LEA && mInstructions[i]->mSrc[0].mTemp == -1) + else if (mInstructions[i]->mCode == IC_CONSTANT || (mInstructions[i]->mCode == IC_LEA && mInstructions[i]->mSrc[0].mTemp == -1)) { InterInstruction* ins(mInstructions[i]); int j = i; @@ -7879,6 +7979,7 @@ void InterCodeBasicBlock::PeepholeOptimization(void) i--; } +#endif bool changed; do diff --git a/oscar64/InterCodeGenerator.cpp b/oscar64/InterCodeGenerator.cpp index 1764d5a..82d1ea4 100644 --- a/oscar64/InterCodeGenerator.cpp +++ b/oscar64/InterCodeGenerator.cpp @@ -905,6 +905,10 @@ InterCodeGenerator::ExValue InterCodeGenerator::TranslateExpression(Declaration* ins->mDst.mTemp = proc->AddTemporary(ins->mDst.mType); ins->mConst.mOperandSize = dec->mSize; ins->mConst.mIntConst = dec->mOffset; + + if (dec->mType == DT_VARIABLE_REF) + dec = dec->mBase; + ins->mConst.mVarIndex = dec->mVarIndex; int ref = 1; @@ -945,7 +949,7 @@ InterCodeGenerator::ExValue InterCodeGenerator::TranslateExpression(Declaration* if (!(dec->mBase->mFlags & DTF_DEFINED)) mErrors->Error(dec->mLocation, EERR_VARIABLE_TYPE, "Undefined variable type"); - return ExValue(dec->mBase, ins->mDst.mTemp, ref); + return ExValue(exp->mDecType, ins->mDst.mTemp, ref); } @@ -3364,6 +3368,23 @@ void InterCodeGenerator::BuildInitializer(InterCodeModule * mod, uint8* dp, int ref.mRefOffset = 0; variable->mLinkerObject->AddReference(ref); } break; + + case DT_VARIABLE_REF: + { + if (!dec->mBase->mLinkerObject) + { + InitGlobalVariable(mod, dec->mBase); + } + + LinkerReference ref; + ref.mObject = variable->mLinkerObject; + ref.mOffset = offset; + ref.mFlags = LREF_LOWBYTE | LREF_HIGHBYTE; + ref.mRefObject = dec->mBase->mLinkerObject; + ref.mRefOffset = dec->mOffset; + variable->mLinkerObject->AddReference(ref); + } break; + } } } diff --git a/oscar64/NativeCodeGenerator.cpp b/oscar64/NativeCodeGenerator.cpp index 0e66f42..2ce8a8a 100644 --- a/oscar64/NativeCodeGenerator.cpp +++ b/oscar64/NativeCodeGenerator.cpp @@ -641,8 +641,12 @@ void NativeCodeInstruction::ReplaceYRegWithXReg(void) break; } + if (mMode == ASMIM_ABSOLUTE_Y) + { + assert(HasAsmInstructionMode(mType, ASMIM_ABSOLUTE_X)); mMode = ASMIM_ABSOLUTE_X; + } } bool NativeCodeInstruction::ChangesYReg(void) const @@ -8928,7 +8932,10 @@ bool NativeCodeBasicBlock::ReduceLocalYPressure(void) else if (ins.mType == ASMIT_STY) ins.mType = ASMIT_STX; else if (ins.mMode == ASMIM_ABSOLUTE_Y) + { + assert(HasAsmInstructionMode(ins.mType, ASMIM_ABSOLUTE_X)); ins.mMode = ASMIM_ABSOLUTE_X; + } else if (ins.mType == ASMIT_LDY || ins.mType == ASMIT_TAY) active = false; } @@ -10941,6 +10948,20 @@ bool NativeCodeBasicBlock::OptimizeSimpleLoopInvariant(NativeCodeProcedure* proc changed = true; } + + i = mIns.Size() - 1; + while (i >= 0 && !mIns[i].ChangesAccu() && mIns[i].mType != ASMIT_STA) + i--; + if (i >= 0 && mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[ai].mAddress) + { + if (!prevBlock) + return OptimizeSimpleLoopInvariant(proc); + + prevBlock->mIns.Push(mIns[ai]); + mIns.Remove(ai); + + changed = true; + } } } @@ -12769,15 +12790,18 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) { if (!mIns[i + 1].RequiresYReg() && !mIns[i + 1].ChangesYReg() && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) { - if (!mIns[i].MayBeChangedOnAddress(mIns[i + 1])) + if (mIns[i].mMode != ASMIM_ABSOLUTE_X || !mIns[i + 1].ChangesXReg()) { - if (mIns[i + 1].SameEffectiveAddress(mIns[i])) - mIns[i + 1].mLive |= LIVE_MEM; + if (!mIns[i].MayBeChangedOnAddress(mIns[i + 1])) + { + if (mIns[i + 1].SameEffectiveAddress(mIns[i])) + mIns[i + 1].mLive |= LIVE_MEM; - NativeCodeInstruction ins = mIns[i]; - mIns[i] = mIns[i + 1]; - mIns[i + 1] = ins; - mIns[i + 1].mLive |= mIns[i].mLive; + NativeCodeInstruction ins = mIns[i]; + mIns[i] = mIns[i + 1]; + mIns[i + 1] = ins; + mIns[i + 1].mLive |= mIns[i].mLive; + } } } } @@ -12785,15 +12809,18 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) { if (!mIns[i + 1].RequiresXReg() && !mIns[i + 1].ChangesXReg() && !(mIns[i + 1].mLive & LIVE_CPU_REG_Z)) { - if (!mIns[i].MayBeChangedOnAddress(mIns[i + 1])) + if (mIns[i].mMode != ASMIM_ABSOLUTE_Y || !mIns[i + 1].ChangesYReg()) { - if (mIns[i + 1].SameEffectiveAddress(mIns[i])) - mIns[i + 1].mLive |= LIVE_MEM; + if (!mIns[i].MayBeChangedOnAddress(mIns[i + 1])) + { + if (mIns[i + 1].SameEffectiveAddress(mIns[i])) + mIns[i + 1].mLive |= LIVE_MEM; - NativeCodeInstruction ins = mIns[i]; - mIns[i] = mIns[i + 1]; - mIns[i + 1] = ins; - mIns[i + 1].mLive |= mIns[i].mLive; + NativeCodeInstruction ins = mIns[i]; + mIns[i] = mIns[i + 1]; + mIns[i + 1] = ins; + mIns[i + 1].mLive |= mIns[i].mLive; + } } } } @@ -12950,6 +12977,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) { if (mIns[j].mMode == ASMIM_ABSOLUTE_Y) { + assert(HasAsmInstructionMode(mIns[j].mType, ASMIM_ABSOLUTE_X)); mIns[j].mMode = ASMIM_ABSOLUTE_X; changed = true; } @@ -12963,6 +12991,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) { if (mIns[j].mMode == ASMIM_ABSOLUTE_X) { + assert(HasAsmInstructionMode(mIns[j].mType, ASMIM_ABSOLUTE_Y)); mIns[j].mMode = ASMIM_ABSOLUTE_Y; changed = true; } @@ -13527,7 +13556,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) progress = true; } else if ( - mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_ABSOLUTE_X) && mIns[i + 1].mType == ASMIT_TAY && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) { mIns[i + 0].mType = ASMIT_LDY; mIns[i + 0].mLive |= mIns[i + 1].mLive; @@ -13535,7 +13564,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) progress = true; } else if ( - mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y) && mIns[i + 1].mType == ASMIT_TAX && !(mIns[i + 1].mLive & LIVE_CPU_REG_A)) { mIns[i + 0].mType = ASMIT_LDX; mIns[i + 0].mLive |= mIns[i + 1].mLive; @@ -13543,7 +13572,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) progress = true; } else if ( - mIns[i + 0].mType == ASMIT_LDY && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 0].mType == ASMIT_LDY && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_ABSOLUTE_X) && mIns[i + 1].mType == ASMIT_TYA && !(mIns[i + 1].mLive & LIVE_CPU_REG_Y)) { mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= mIns[i + 1].mLive; @@ -13551,7 +13580,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) progress = true; } else if ( - mIns[i + 0].mType == ASMIT_LDX && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && + mIns[i + 0].mType == ASMIT_LDX && (mIns[i + 0].mMode == ASMIM_IMMEDIATE || mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE || mIns[i + 0].mMode == ASMIM_ABSOLUTE_Y) && mIns[i + 1].mType == ASMIT_TXA && !(mIns[i + 1].mLive & LIVE_CPU_REG_X)) { mIns[i + 0].mType = ASMIT_LDA; mIns[i + 0].mLive |= mIns[i + 1].mLive; @@ -14355,7 +14384,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) else if ( mIns[i + 0].mType == ASMIT_TAX && mIns[i + 1].mType == ASMIT_TAY && - mIns[i + 2].mMode == ASMIM_ABSOLUTE_Y && (mIns[i + 2].mLive & LIVE_CPU_REG_X) && !(mIns[i + 2].mLive & LIVE_CPU_REG_Y)) + mIns[i + 2].mMode == ASMIM_ABSOLUTE_Y && (mIns[i + 2].mLive & LIVE_CPU_REG_X) && !(mIns[i + 2].mLive & LIVE_CPU_REG_Y) && HasAsmInstructionMode(mIns[i + 2].mType, ASMIM_ABSOLUTE_X)) { mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; mIns[i + 2].mMode = ASMIM_ABSOLUTE_X; @@ -14364,7 +14393,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) else if ( mIns[i + 0].mType == ASMIT_TAY && mIns[i + 1].mType == ASMIT_TAX && - mIns[i + 2].mMode == ASMIM_ABSOLUTE_Y && (mIns[i + 2].mLive & LIVE_CPU_REG_X) && !(mIns[i + 2].mLive & LIVE_CPU_REG_Y)) + mIns[i + 2].mMode == ASMIM_ABSOLUTE_Y && (mIns[i + 2].mLive & LIVE_CPU_REG_X) && !(mIns[i + 2].mLive & LIVE_CPU_REG_Y) && HasAsmInstructionMode(mIns[i + 2].mType, ASMIM_ABSOLUTE_X)) { mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 2].mMode = ASMIM_ABSOLUTE_X; @@ -14435,6 +14464,22 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) mIns[i + 0].mLive |= LIVE_CPU_REG_A; progress = true; } +#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 + 1].mLive & LIVE_CPU_REG_A) && + mIns[i + 2].mType == ASMIT_CPX && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 2].mAddress && !(mIns[i + 2].mLive & LIVE_MEM)) + { + mIns[i + 1] = mIns[i + 0]; + mIns[i + 1].mType = ASMIT_CMP; + mIns[i + 1].mLive |= mIns[i + 2].mLive; + mIns[i + 0].mType = ASMIT_TXA; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + progress = true; + } +#endif + #if 1 if ( mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress <= 1 && @@ -14916,8 +14961,26 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) progress = true; } #endif +#if 1 + else if ( + mIns[i + 0].IsShift() && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_CLC && + mIns[i + 2].mType == ASMIT_LDA && (mIns[i + 2].mMode == ASMIM_IMMEDIATE || mIns[i + 2].mMode == ASMIM_IMMEDIATE_ADDRESS) && + mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_ZERO_PAGE && mIns[i + 3].mAddress == mIns[i + 0].mAddress && + !(mIns[i + 3].mLive & LIVE_MEM)) + { + mIns[i + 3] = mIns[i + 2]; + mIns[i + 2] = mIns[i + 1]; + mIns[i + 1].mType = mIns[i + 0].mType; + mIns[i + 0].mType = ASMIT_LDA; + mIns[i + 3].mType = ASMIT_ADC; - + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mLive |= LIVE_CPU_REG_A; + progress = true; + } +#endif #if 1 if ( mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && @@ -14987,6 +15050,54 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) progress = true; } #endif + else if ( + mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode != ASMIM_ZERO_PAGE && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_SEC && + mIns[i + 3].mType == ASMIT_LDA && !mIns[i + 3].UsesZeroPage(mIns[i + 1].mAddress) && + mIns[i + 4].mType == ASMIT_SBC && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 1].mAddress && !(mIns[i + 4].mLive & LIVE_MEM)) + { + mIns[i + 4] = mIns[i + 0]; + mIns[i + 4].mType = ASMIT_SBC; + + mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + progress = true; + } + else if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + !mIns[i + 1].ChangesAccu() && + mIns[i + 2].mType == ASMIT_LDA && + mIns[i + 3].mType == ASMIT_CLC && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + + mIns[i + 3] = mIns[i + 2]; + mIns[i + 3].mType = ASMIT_ADC; + mIns[i + 2].mType = ASMIT_CLC; + mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 4].mType = ASMIT_NOP; + mIns[i + 4].mMode = ASMIM_IMPLIED; + progress = true; + } + else if ( + mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + !mIns[i + 1].ChangesAccu() && + mIns[i + 2].mType == ASMIT_CLC && + mIns[i + 3].mType == ASMIT_LDA && + mIns[i + 4].mType == ASMIT_ADC && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 0].mAddress) + { + mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mLive |= LIVE_CPU_REG_A; + mIns[i + 2].mLive |= LIVE_CPU_REG_A; + + mIns[i + 3].mType = ASMIT_ADC; + mIns[i + 4].mType = ASMIT_NOP; + mIns[i + 4].mMode = ASMIM_IMPLIED; + progress = true; + } #if 1 else if ( mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0 && @@ -15016,20 +15127,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) progress = true; } } - else if ( - mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode != ASMIM_ZERO_PAGE && - mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && - mIns[i + 2].mType == ASMIT_SEC && - mIns[i + 3].mType == ASMIT_LDA && !mIns[i + 3].UsesZeroPage(mIns[i + 1].mAddress) && - mIns[i + 4].mType == ASMIT_SBC && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 1].mAddress && !(mIns[i + 4].mLive & LIVE_MEM)) - { - mIns[i + 4] = mIns[i + 0]; - mIns[i + 4].mType = ASMIT_SBC; - mIns[i + 0].mType = ASMIT_NOP; mIns[i + 0].mMode = ASMIM_IMPLIED; - mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; - progress = true; - } #endif } @@ -16351,6 +16449,7 @@ void NativeCodeProcedure::Optimize(void) #if 1 else if (step == 4) { +#if 1 #if 1 int xregs[256], yregs[256]; for (int i = 0; i < 256; i++) @@ -16413,7 +16512,7 @@ void NativeCodeProcedure::Optimize(void) ymapped = true; } } - +#endif if (!changed) { ResetVisited(); diff --git a/oscar64/Parser.cpp b/oscar64/Parser.cpp index 922df17..6c0f1e0 100644 --- a/oscar64/Parser.cpp +++ b/oscar64/Parser.cpp @@ -611,7 +611,7 @@ Declaration * Parser::CopyConstantInitializer(int offset, Declaration* dtype, Ex dec->mOffset = offset; } } - else if (dtype->mType == DT_TYPE_POINTER && dec->mType == DT_VARIABLE && dec->mBase->mType == DT_TYPE_ARRAY && (dec->mFlags & DTF_GLOBAL)) + else if (dec && dtype->mType == DT_TYPE_POINTER && (dec->mType == DT_VARIABLE || dec->mType == DT_VARIABLE_REF) && exp->mDecType->mType == DT_TYPE_ARRAY && (dec->mFlags & DTF_GLOBAL)) { if (dtype->CanAssign(exp->mDecType)) { @@ -867,7 +867,7 @@ Expression* Parser::ParseInitExpression(Declaration* dtype) } else if (dtype->mType == DT_TYPE_POINTER) { - if (exp->mDecValue->mType == DT_CONST_ADDRESS) + if (exp->mDecValue->mType == DT_CONST_ADDRESS || exp->mDecValue->mType == DT_CONST_POINTER) ; else if (exp->mDecValue->mType == DT_CONST_FUNCTION) { @@ -1534,7 +1534,7 @@ Expression* Parser::ParsePostfixExpression(void) { nexp->mDecValue = mdec; nexp->mDecType = mdec->mBase; - exp = nexp; + exp = nexp->ConstantFold(mErrors); } else mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Struct member identifier not found", mScanner->mTokenIdent->mString); @@ -1563,7 +1563,7 @@ Expression* Parser::ParsePostfixExpression(void) { nexp->mDecValue = mdec; nexp->mDecType = mdec->mBase; - exp = nexp; + exp = nexp->ConstantFold(mErrors); } else mErrors->Error(mScanner->mLocation, EERR_OBJECT_NOT_FOUND, "Struct member identifier not found", mScanner->mTokenIdent->mString); diff --git a/oscar64/oscar64.cpp b/oscar64/oscar64.cpp index 3737ac1..9bbd5e4 100644 --- a/oscar64/oscar64.cpp +++ b/oscar64/oscar64.cpp @@ -73,7 +73,7 @@ int main2(int argc, const char** argv) #else strcpy(strProductName, "oscar64"); - strcpy(strProductVersion, "1.4.88"); + strcpy(strProductVersion, "1.4.89"); #ifdef __APPLE__ uint32_t length = sizeof(basePath); diff --git a/oscar64/oscar64.rc b/oscar64/oscar64.rc index 10ed4ee..6f1069a 100644 --- a/oscar64/oscar64.rc +++ b/oscar64/oscar64.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,4,88,0 - PRODUCTVERSION 1,4,88,0 + FILEVERSION 1,4,89,0 + PRODUCTVERSION 1,4,89,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,12 +43,12 @@ BEGIN BEGIN VALUE "CompanyName", "oscar64" VALUE "FileDescription", "oscar64 compiler" - VALUE "FileVersion", "1.4.88.0" + VALUE "FileVersion", "1.4.89.0" VALUE "InternalName", "oscar64.exe" VALUE "LegalCopyright", "Copyright (C) 2021" VALUE "OriginalFilename", "oscar64.exe" VALUE "ProductName", "oscar64" - VALUE "ProductVersion", "1.4.88.0" + VALUE "ProductVersion", "1.4.89.0" END END BLOCK "VarFileInfo" diff --git a/oscar64setup/oscar64setup.vdproj b/oscar64setup/oscar64setup.vdproj index b4913a2..4684183 100644 --- a/oscar64setup/oscar64setup.vdproj +++ b/oscar64setup/oscar64setup.vdproj @@ -52,6 +52,12 @@ } "Entry" { + "MsmKey" = "8:_08F70A83B36D428A9F0C9EFC69787FF1" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_0D8B657E4A954DBFAF14055CDFFB384C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -232,6 +238,12 @@ } "Entry" { + "MsmKey" = "8:_4A1182DE6E79476B9FE3EAE072252A90" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_4B8FC526E6CC47FC8321D0191BFDBEDC" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -670,6 +682,12 @@ } "Entry" { + "MsmKey" = "8:_DEADBEA270134B77800770802B21859C" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_E218D776D9014F99BE2B046AEF2D6E8B" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -989,6 +1007,26 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_08F70A83B36D428A9F0C9EFC69787FF1" + { + "SourcePath" = "8:..\\samples\\resources\\connect4sprites.bin" + "TargetName" = "8:connect4sprites.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "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}:_0D8B657E4A954DBFAF14055CDFFB384C" { "SourcePath" = "8:..\\include\\crt.c" @@ -1589,6 +1627,26 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4A1182DE6E79476B9FE3EAE072252A90" + { + "SourcePath" = "8:..\\samples\\resources\\connect4chars.bin" + "TargetName" = "8:connect4chars.bin" + "Tag" = "8:" + "Folder" = "8:_758C6547998745659548D0656D380FEA" + "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}:_4B8FC526E6CC47FC8321D0191BFDBEDC" { "SourcePath" = "8:..\\samples\\resources\\scifiglyph.bin" @@ -3049,6 +3107,26 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_DEADBEA270134B77800770802B21859C" + { + "SourcePath" = "8:..\\samples\\games\\connectfour.c" + "TargetName" = "8:connectfour.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}:_E218D776D9014F99BE2B046AEF2D6E8B" { "SourcePath" = "8:..\\include\\stdlib.c" @@ -3674,15 +3752,15 @@ { "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:oscar64" - "ProductCode" = "8:{1239BFD2-7316-4A0C-804E-2E085AE3A204}" - "PackageCode" = "8:{E3D1BD76-D6F1-4FC7-9448-733781DFB84A}" + "ProductCode" = "8:{96DACD5F-2EC0-4296-9AFE-FB3F0A377D2A}" + "PackageCode" = "8:{49C9E61D-9B14-437A-A945-39BFE6ED8516}" "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.4.88" + "ProductVersion" = "8:1.4.89" "Manufacturer" = "8:oscar64" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:" diff --git a/samples/games/build.sh b/samples/games/build.sh index 4623e79..3306af4 100644 --- a/samples/games/build.sh +++ b/samples/games/build.sh @@ -4,4 +4,5 @@ ../../bin/oscar64 maze3d.c -n ../../bin/oscar64 missile.c -O3 -n ../../bin/oscar64 breakout.c -n +../../bin/oscar64 connectfour.c -n diff --git a/samples/games/connectfour.c b/samples/games/connectfour.c new file mode 100644 index 0000000..09f6968 --- /dev/null +++ b/samples/games/connectfour.c @@ -0,0 +1,819 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Include charset and sprite resources + +char charset[2048] = { + #embed "../resources/connect4chars.bin" +}; + +char spriteset[2048] = { + #embed "../resources/connect4sprites.bin" +}; + +// Address ov video data + +byte * const Screen = (byte *)0xc800; +byte * const Font = (byte *)0xd000; +byte * const Color = (byte *)0xd800; +byte * const Sprites = (byte *)0xd800; + +// Values for player 1 pieces and player 2 pieces. +// The values 1 and 5 are selected so that the sum of the +// values in a row of four will add up to a unique value +// in the range of 0..24 for ranking + +#define PLAYER1_INC 1 +#define PLAYER2_INC 5 + +// Current state of the board + +struct Board +{ + char fcols[48]; // Pieces in the 6x7 grid using 48 to have a multplier of 8 + char ffree[7]; // Number of free places in each column +} board; + +enum GameState +{ + GS_READY, // Getting ready + + GS_PLAYER_MOVE, // The players move + GS_COMPUTER_MOVE, // The computers move + + GS_PLAYER_WIN, // The player wins + GS_COMPUTER_WIN, // The computer wins + GS_GAME_OVER // No more moves +}; + +// Draw the board into the CharWin using the small +// character set + +void board_draw(CharWin * cwin) +{ + for(char y=0; y<6; y++) + { + for(char x=0; x<7; x++) + { + if (board.fcols[8 * y + x] == PLAYER1_INC) + cwin_putat_char_raw(cwin, x, y, 82, 10); + else if (board.fcols[8 * y + x] == PLAYER2_INC) + cwin_putat_char_raw(cwin, x, y, 82, 15); + else + cwin_putat_char_raw(cwin, x, y, 82, 8); + } + + // Right most column + + cwin_putat_char_raw(cwin, 7, y, 83, 8); + } +} + +// Draw a big piece or place with the given color, the +// color 0 denotes an empty place. Empty places use different +// tiles to allow sprite priority to obscure the falling pieces + +void board_draw_item(CharWin * cwin, char x, char y, char c) +{ + char cx = x * 3 + 1, cy = y * 3 + 2; + + if (c) + { + cwin_putat_char_raw(cwin, cx + 0, cy + 0, 73, c); + cwin_putat_char_raw(cwin, cx + 1, cy + 0, 74, c); + cwin_putat_char_raw(cwin, cx + 2, cy + 0, 75, c); + + cwin_putat_char_raw(cwin, cx + 0, cy + 1, 76, c); + cwin_putat_char_raw(cwin, cx + 1, cy + 1, 77, c); + cwin_putat_char_raw(cwin, cx + 2, cy + 1, 78, c); + + cwin_putat_char_raw(cwin, cx + 0, cy + 2, 79, c); + cwin_putat_char_raw(cwin, cx + 1, cy + 2, 80, c); + cwin_putat_char_raw(cwin, cx + 2, cy + 2, 81, c); + } + else + { + cwin_putat_char_raw(cwin, cx + 0, cy + 0, 64, 14); + cwin_putat_char_raw(cwin, cx + 1, cy + 0, 65, 14); + cwin_putat_char_raw(cwin, cx + 2, cy + 0, 66, 14); + + cwin_putat_char_raw(cwin, cx + 0, cy + 1, 67, 14); + cwin_putat_char_raw(cwin, cx + 1, cy + 1, 68, 14); + cwin_putat_char_raw(cwin, cx + 2, cy + 1, 69, 14); + + cwin_putat_char_raw(cwin, cx + 0, cy + 2, 70, 14); + cwin_putat_char_raw(cwin, cx + 1, cy + 2, 71, 14); + cwin_putat_char_raw(cwin, cx + 2, cy + 2, 72, 14); + } +} + +// Draw the big board + +void board_draw_main(CharWin * cwin) +{ + for(char y=0; y<6; y++) + { + for(char x=0; x<7; x++) + { + if (board.fcols[8 * y + x] == PLAYER1_INC) + board_draw_item(cwin, x, y, 10); + else if (board.fcols[8 * y + x] == PLAYER2_INC) + board_draw_item(cwin, x, y, 15); + else + board_draw_item(cwin, x, y, 0); + } + } +} + +// Draw the decoration of the big board + +void board_init_main(CharWin * cwin) +{ + // Frame around the board + + cwin_putat_char_raw(cwin, 0, 1, 84, 14); + cwin_fill_rect_raw(cwin, 1, 1, 21, 1, 85, 14); + cwin_putat_char_raw(cwin, 22, 1, 86, 14); + cwin_fill_rect_raw(cwin, 0, 2, 1, 18, 87, 14); + cwin_fill_rect_raw(cwin, 22, 2, 1, 18, 88, 14); + cwin_putat_char_raw(cwin, 0, 20, 89, 14); + cwin_fill_rect_raw(cwin, 1, 20, 21, 1, 90, 14); + cwin_putat_char_raw(cwin, 22, 20, 91, 14); + + // Fields in the board + + for(char x=0; x<7; x++) + cwin_putat_char_raw(cwin, 3 * x + 2, 0, '1' + x, 1); + + board_draw_main(cwin); +} + +// Play animation of a dropped piece + +void item_drop_anim(CharWin * cwin, char x, char y, char c) +{ + // Initial position + + int ix = (cwin->sx + 3 * x + 1) * 8 + 24, iy = (cwin->sy + 2) * 8 + 20; + + // Show sprite at start position + spr_set(0, true, ix, iy, 97, c, true, false, false); + + // Set sprite priority to be behind background + vic.spr_priority = 0x01; + + // Speed and target position, using four fractional bits + int vy = 0, aiy = iy * 16, ty = ((cwin->sy + y * 3 + 2) * 8 + 50) * 16; + + // Bounce three times back when reaching bottom + char bounce = 3; + + // Loop through the bounces + while (bounce > 0) + { + // Let gravity do its thing of accelerating + vy += 3; + + // Add vertical velocity to position + aiy += vy; + + // Reached the bottom yet? + if (aiy > ty) + { + // Reflect position + aiy = 2 * ty - aiy; + + // Reflect speed + vy = - vy * 5 >> 4; + + // One bounce down + bounce--; + } + + // Move sprite and wait for display + spr_move(0, ix, aiy >> 4); + vic_waitFrame(); + } +} + +// Play column selection animation + +void column_select_anim(CharWin * cwin, char x0, char x1, char c) +{ + // Start position + int ix = (cwin->sx + 3 * x0 + 1) * 8 + 24, iy = (cwin->sy + 2) * 8 + 20; + + // Target position + int tx = (cwin->sx + 3 * x1 + 1) * 8 + 24; + + // Show sprite at start position + spr_set(0, true, ix, iy, 97, c, true, false, false); + + // Set sprite priority to be behind background + vic.spr_priority = 0x01; + + // Not at target position already? + if (ix != tx) + { + // Eight frames for movement + for(int i=1; i<=8; i++) + { + vic_waitFrame(); + // Move sprite along path + spr_move(0, (ix * (8 - i) + tx * i) >> 3, iy); + } + } +} + +// Initialize the board to empty + +void board_init(void) +{ + // Column has six free slots + for(char x=0; x<7; x++) + board.ffree[x] = 6; + + // Clear all slots + for(char i=0; i<48; i++) + board.fcols[i] = 0; +} + +// Drop one piece of player p down column x + +inline bool board_drop(char x, bool p) +{ + // Check for free space + if (board.ffree[x]) + { + // Fill one slot + char y = --(board.ffree[x]); + board.fcols[8 * y + x] = p ? PLAYER2_INC : PLAYER1_INC; + + // Success + return true; + } + return false; +} + +// Remove the top piece from column x + +inline void board_undrop(char x) +{ + // Free one slot + char y = (board.ffree[x])++; + board.fcols[8 * y + x] = 0x00; +} + +// Score for rows with 0, 1, 2, and 3 elements of same color + +static const int score[5] = {0, 1, 8, 128, 10000}; + +// Score index is calculated (25 * rv + 5 * p1 + p2) with rv the +// value range of the row, p1 and p2 number of pieces from player 1 or 2 + +int fscore[125]; + +// Indices of the board positions of four slots of each of the 69 +// 4 slot rows in the board. The fifth value is the value range +// of the row + +char frows[5][69]; + +#pragma align(fscore, 256) + +// Tables for opening libary for computer move 1, 2, 3 and 4 + +const char open1 = 3; +const char open2[4 * 7] = +{ + 4, 3, 3, 3, 4, 3, 3, 3, 5, 3, 3, 4, 3, 3, 3, 3, 2, 3, 2, 2, 2, 4, 2, 4, 3, 2, 4, 2 +}; + +const char open3[4 * 7 * 7] = { + 0, 5, 2, 2, 5, 3, 2, 5, 4, 2, 4, 3, 3, 5, 2, 2, 3, 2, 3, 2, 2, 4, 5, 2, 4, 4, 3, 2, + 3, 3, 3, 3, 3, 3, 4, 1, 3, 2, 5, 3, 2, 1, 2, 5, 2, 2, 4, 1, 4, 5, 4, 2, 4, 3, 3, 5, + 4, 1, 3, 4, 4, 4, 3, 2, 1, 3, 2, 3, 2, 3, 5, 5, 1, 5, 4, 3, 3, 3, 4, 3, 3, 5, 3, 3, + 3, 5, 2, 3, 4, 1, 3, 5, 4, 3, 1, 4, 3, 5, 2, 2, 3, 2, 3, 2, 2, 2, 1, 3, 2, 3, 2, 3, + 3, 1, 3, 2, 2, 3, 3, 2, 1, 3, 2, 2, 2, 2, 3, 3, 3, 2, 1, 3, 3, 3, 3, 1, 3, 3, 2, 3, + 2, 3, 3, 3, 3, 3, 3, 3, 2, 1, 2, 3, 4, 3, 2, 4, 3, 2, 2, 2, 2, 1, 0, 2, 4, 3, 4, 4, + 4, 4, 1, 2, 5, 2, 2, 2, 2, 3, 2, 4, 6, 5, 4, 4, 4, 4, 3, 2, 4, 3, 2, 3, 4, 5, 4, 3, +}; + +const char open4[4 * 7 * 7 * 7] = { + 2, 5, 2, 2, 5, 3, 2, 2, 2, 6, 2, 2, 2, 2, 0, 4, 4, 3, 2, 3, 3, 5, 5, 5, 5, 5, 1, 1, + 2, 2, 6, 2, 2, 2, 2, 0, 4, 4, 4, 3, 5, 4, 1, 5, 1, 1, 1, 1, 1, 0, 4, 2, 4, 3, 4, 5, + 5, 1, 4, 5, 5, 3, 2, 3, 3, 1, 1, 1, 1, 3, 5, 5, 4, 5, 5, 4, 4, 3, 3, 3, 1, 3, 3, 3, + 3, 3, 3, 1, 3, 3, 3, 5, 4, 2, 5, 3, 1, 4, 0, 3, 6, 2, 3, 3, 3, 3, 3, 1, 1, 1, 1, 3, + 3, 3, 3, 2, 3, 3, 3, 2, 1, 6, 2, 4, 5, 2, 3, 3, 3, 2, 3, 3, 3, 3, 1, 5, 5, 3, 3, 3, + 3, 3, 0, 2, 3, 3, 3, 0, 5, 2, 2, 5, 1, 2, 4, 4, 2, 4, 5, 4, 2, 2, 2, 3, 2, 2, 2, 2, + 2, 5, 5, 2, 2, 1, 2, 4, 3, 4, 4, 3, 3, 4, 1, 3, 2, 1, 4, 4, 1, 4, 5, 4, 4, 4, 2, 4, + 0, 3, 3, 4, 3, 3, 3, 3, 3, 3, 1, 3, 5, 3, 3, 3, 3, 2, 2, 4, 3, 4, 3, 4, 4, 3, 3, 4, + 3, 3, 2, 3, 3, 5, 6, 3, 5, 4, 5, 5, 3, 3, 3, 3, 2, 3, 3, 3, 5, 0, 5, 3, 4, 4, 3, 2, + 3, 3, 3, 1, 3, 3, 3, 3, 1, 5, 5, 3, 3, 3, 3, 3, 2, 1, 4, 3, 1, 3, 3, 3, 4, 3, 3, 3, + 2, 2, 3, 2, 5, 5, 4, 2, 5, 3, 1, 4, 2, 1, 0, 2, 4, 4, 3, 2, 4, 5, 4, 2, 5, 3, 1, 4, + 3, 3, 0, 2, 3, 3, 3, 4, 2, 4, 4, 2, 2, 4, 3, 3, 3, 4, 6, 3, 3, 2, 5, 3, 1, 4, 2, 1, + 2, 2, 3, 2, 2, 4, 6, 0, 4, 2, 4, 3, 4, 5, 5, 1, 4, 5, 5, 3, 2, 3, 3, 1, 1, 1, 1, 3, + 5, 5, 4, 5, 5, 4, 4, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 5, 4, 2, 5, 3, 1, 4, + 2, 2, 6, 2, 2, 2, 2, 4, 4, 3, 4, 4, 4, 3, 4, 1, 3, 5, 3, 3, 3, 2, 2, 6, 2, 2, 2, 2, + 5, 1, 3, 3, 5, 4, 3, 2, 2, 6, 2, 2, 2, 2, 4, 1, 3, 5, 3, 1, 3, 3, 3, 1, 1, 1, 1, 3, + 6, 2, 3, 1, 4, 5, 4, 3, 3, 3, 2, 3, 3, 3, 1, 2, 3, 1, 4, 1, 1, 3, 3, 3, 4, 3, 2, 3, + 1, 3, 1, 1, 4, 2, 1, 3, 3, 3, 2, 3, 3, 3, 4, 4, 2, 4, 5, 4, 2, 4, 1, 6, 4, 4, 4, 2, + 1, 3, 2, 4, 3, 1, 1, 4, 4, 4, 4, 4, 4, 2, 3, 3, 4, 4, 3, 3, 4, 3, 5, 3, 1, 3, 1, 3, + 5, 2, 2, 5, 4, 3, 5, 3, 3, 3, 1, 3, 5, 3, 3, 1, 4, 4, 5, 3, 3, 3, 1, 3, 2, 2, 2, 3, + 3, 3, 4, 4, 3, 3, 4, 3, 5, 3, 3, 5, 5, 3, 5, 4, 2, 5, 5, 3, 5, 3, 3, 3, 1, 1, 5, 3, + 3, 3, 3, 1, 3, 3, 3, 5, 1, 1, 3, 4, 3, 3, 1, 3, 1, 1, 4, 2, 1, 5, 5, 2, 1, 4, 1, 1, + 5, 4, 2, 5, 5, 3, 5, 3, 3, 2, 3, 5, 5, 1, 3, 3, 3, 5, 3, 3, 3, 5, 4, 2, 5, 3, 1, 4, + 2, 1, 1, 4, 3, 4, 4, 3, 3, 3, 2, 3, 3, 3, 5, 3, 2, 5, 4, 3, 3, 3, 3, 3, 1, 1, 5, 3, + 3, 3, 3, 5, 3, 3, 3, 4, 3, 2, 2, 3, 1, 6, 0, 3, 6, 2, 3, 3, 3, 3, 3, 1, 1, 1, 1, 3, + 3, 3, 3, 2, 3, 3, 3, 2, 1, 6, 2, 4, 5, 2, 3, 3, 3, 2, 3, 3, 3, 3, 1, 5, 5, 3, 3, 3, + 3, 3, 0, 2, 3, 3, 3, 3, 3, 1, 1, 1, 1, 3, 6, 2, 3, 1, 4, 5, 4, 3, 3, 3, 2, 3, 3, 3, + 1, 2, 3, 1, 4, 1, 1, 3, 3, 3, 4, 3, 2, 3, 1, 3, 1, 1, 4, 2, 1, 3, 3, 3, 2, 3, 3, 3, + 3, 3, 3, 4, 3, 3, 3, 2, 3, 1, 3, 0, 1, 1, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 2, 2, 3, + 4, 4, 4, 2, 2, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 2, 2, 2, 2, + 1, 3, 2, 4, 3, 1, 1, 0, 1, 2, 2, 4, 5, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 2, 2, 3, 3, 2, 3, 3, 2, 2, 3, 2, 3, 1, 3, 4, 3, 3, 3, 1, 1, 4, 4, 2, 4, 2, + 3, 4, 3, 4, 3, 4, 4, 3, 2, 3, 2, 3, 3, 3, 1, 2, 2, 2, 4, 4, 2, 3, 4, 4, 4, 3, 5, 3, + 3, 2, 4, 4, 3, 3, 3, 3, 1, 5, 5, 3, 3, 3, 1, 3, 1, 1, 4, 2, 1, 3, 1, 1, 3, 3, 1, 3, + 2, 3, 3, 2, 2, 3, 3, 3, 4, 4, 4, 3, 5, 3, 3, 3, 1, 2, 2, 5, 3, 3, 1, 3, 5, 3, 3, 3, + 1, 3, 3, 3, 4, 3, 3, 3, 3, 1, 1, 2, 1, 3, 0, 1, 3, 3, 4, 3, 3, 2, 3, 3, 2, 2, 3, 2, + 3, 2, 4, 4, 3, 3, 3, 3, 1, 3, 5, 3, 3, 3, 3, 3, 3, 2, 3, 3, 6, 0, 2, 1, 2, 3, 4, 4, + 4, 1, 3, 3, 4, 4, 2, 4, 3, 2, 4, 3, 3, 4, 3, 3, 2, 4, 4, 4, 3, 3, 2, 1, 4, 3, 3, 3, + 4, 2, 1, 4, 5, 4, 2, 4, 2, 1, 2, 3, 4, 4, 2, 2, 1, 2, 1, 2, 2, 1, 1, 3, 3, 3, 5, 1, + 2, 5, 3, 2, 5, 3, 3, 2, 3, 1, 1, 1, 6, 2, 1, 4, 4, 1, 4, 6, 5, 2, 2, 1, 6, 6, 2, 4, + 2, 2, 1, 2, 5, 4, 2, 4, 3, 2, 4, 3, 3, 4, 4, 3, 3, 3, 3, 1, 1, 1, 0, 3, 3, 3, 3, 3, + 1, 0, 2, 2, 4, 5, 2, 1, 0, 3, 1, 3, 1, 3, 1, 0, 2, 5, 2, 2, 5, 1, 0, 3, 2, 2, 5, 2, + 2, 2, 1, 4, 4, 4, 2, 2, 4, 0, 2, 4, 2, 4, 5, 4, 2, 1, 5, 4, 1, 2, 1, 2, 2, 2, 4, 2, + 5, 2, 1, 5, 4, 2, 1, 2, 4, 2, 4, 6, 2, 4, 4, 2, 2, 2, 5, 4, 4, 4, 1, 4, 2, 3, 6, 5, + 1, 4, 4, 1, 4, 6, 5, 3, 5, 3, 5, 3, 6, 5, 2, 1, 2, 4, 4, 6, 5, 3, 3, 3, 3, 3, 6, 5, + 5, 5, 3, 3, 3, 3, 2, 2, 3, 3, 2, 4, 3, 2, 4, 2, 1, 4, 5, 4, 2, 2, 4, 0, 0, 5, 4, 2, + 1, 0, 2, 5, 2, 2, 5, 4, 0, 5, 5, 5, 3, 4, 3, 3, 1, 4, 3, 1, 2, 5, 1, 3, 3, 3, 5, 5, + 2, 2, 5, 4, 5, 4, 4, 2, 2, 3, 4, 5, 4, 2, 2, 2, 1, 2, 5, 4, 2, 3, 3, 3, 2, 5, 4, 3, + 3, 2, 2, 2, 4, 3, 3, 2, 3, 3, 2, 4, 3, 2, 4, 2, 2, 3, 3, 5, 2, 2, 2, 3, 4, 5, 4, 6, +}; + +// Init the ai tables +void ai_init(void) +{ + // Loop over 5 value ranges, and 0 to 4 pieces of each player + // in a row + + for(char k=0; k<5; k++) + { + for(char i=0; i<5; i++) + { + for(char j=0; j<5; j++) + { + if (i && j) + fscore[25 * k + PLAYER2_INC * i + j] = 0; + else if (i == 4 || j == 4) + fscore[25 * k + PLAYER2_INC * i + j] = score[i] - score[j]; + else + fscore[25 * k + PLAYER2_INC * i + j] = (score[i] - score[j]) * (1 + k); + } + } + } + + // Create the indices of all 69 rows + + char p = 0; + + // Vertical rows + for(char x=0; x<7; x++) + { + for(char y=0; y<3; y++) + { + frows[0][p] = x + 8 * y; + frows[1][p] = x + 8 * y + 8; + frows[2][p] = x + 8 * y + 16; + frows[3][p] = x + 8 * y + 24; + frows[4][p] = 25 * (y + 1); + p++; + } + } + + for(char x=0; x<4; x++) + { + // Horizontal rows + for(char y=0; y<6; y++) + { + frows[0][p] = x + 8 * y; + frows[1][p] = x + 8 * y + 1; + frows[2][p] = x + 8 * y + 2; + frows[3][p] = x + 8 * y + 3; + frows[4][p] = y > 0 ? 25 * (y - 1) : 0; + p++; + } + + // Diagonal down rows + for(char y=0; y<3; y++) + { + frows[0][p] = x + 8 * y; + frows[1][p] = x + 8 * y + 9; + frows[2][p] = x + 8 * y + 18; + frows[3][p] = x + 8 * y + 27; + frows[4][p] = 25 * (y + 1); + p++; + } + + // Diagonal up rows + for(char y=3; y<6; y++) + { + frows[0][p] = x + 8 * y; + frows[1][p] = x + 8 * y - 7; + frows[2][p] = x + 8 * y - 14; + frows[3][p] = x + 8 * y - 21; + frows[4][p] = 25 * (y - 2); + p++; + } + } +} + +// Evaluate the current board state for te player + +int board_eval(bool player) +{ + int score = 0; + + // Loop through all rows + for(char r=0; r<69; r++) + { + // Build score index for row + char sum = + board.fcols[frows[0][r]] + + board.fcols[frows[1][r]] + + board.fcols[frows[2][r]] + + board.fcols[frows[3][r]] + + frows[4][r]; + + // Help compiler to avoid two byte pointer arithmetic + __assume(sum < 128); + + // Add score to board score + score += fscore[sum]; + } + + // Check if there was a row with four winning pieces and + // normalize to +/- 32000 then + + if (score < -8192) + score = -32000; + else if (score >= 8192) + score = 32000; + + // Invert score for other player + + return player ? score : -score; +} + +CharWin cwt, cws; +CharWin cw, cwm; + +char buff[20]; + +static const char optx[7] = {3, 2, 4, 1, 5, 0, 6}; + +// Find a good move for the player using alpha/beta pruning +// see: https://en.wikipedia.org/wiki/Alpha-beta_pruning + +int board_check(char level, int alpha, int beta, bool player) +{ + int best = alpha; + bool checked = false; + + // Current score to cutof early if clear win condition + int val = board_eval(player) + level; + + // Best move + char zx = 0; + + // Check for end of search + if (level < 5 && val < 5000 && val > -5000) + { + // Update current check display at level 3 + if (level == 3) + { + board_draw(&cwt); + itoa(val, buff, 10); + cwin_fill_rect(&cwt, 0, 6, 7, 1, ' ', 1); + cwin_putat_string(&cwt, 0, 6, buff, 1); + } + + // Check all seven columns + for(char i=0; i<7; i++) + { + // Start check in the center, due to higher value of + // pieces there + + char ix = optx[i]; + + // Try to drop a piece + + if (board_drop(ix, player)) + { + // We still have a move + checked = true; + + // Check score of position + int score = - board_check(level + 1, -beta, -best, !player); + + // Better than what we have so far + if (score > best) + { + // Remember + best = score; + zx = ix; + + // Update best move if on level zero + if (level == 0) + { + board_draw(&cw); + itoa(best, buff, 10); + cwin_fill_rect(&cw, 0, 6, 7, 1, ' ', 1); + cwin_putat_string(&cw, 0, 6, buff, 1); + } + + // Check for beta pruning + if (best > beta) + { + board_undrop(ix); + break; + } + } + + // Undo move + board_undrop(ix); + } + } + } + + // Return new position if first move level + if (level == 0) + return zx; + else if (checked) + return best; + else + return val; +} + +// Return a move from the opening library if available + +char board_opening(char step, char * moves) +{ + switch (step) + { + case 1: + return open1; + case 2: + if (moves[0] < 4) + return open2[moves[0] * 7 + moves[1]]; + else + return 6 - open2[(6 - moves[0]) * 7 + (6 - moves[1])]; + + case 3: + if (moves[0] < 4) + return open3[moves[0] * 49 + moves[1] * 7 + moves[2]]; + else + return 6 - open3[(6 - moves[0]) * 49 + (6 - moves[1]) * 7 + (6 - moves[2])]; + + case 4: + if (moves[0] < 4) + return open4[moves[0] * 343 + moves[1] * 49 + moves[2] * 7 + moves[3]]; + else + return 6 - open4[(6 - moves[0]) * 343 + (6 - moves[1]) * 49 + (6 - moves[2]) * 7 + (6 - moves[3])]; + + default: + return 0xff; + } +} + +// Current state of the game +struct Game +{ + GameState state; // State + char count; // Auto continue counter + char posx; // Column osition of new piece + char step; // Number of player moves + char moves[21]; // Pieces placed by player + +} TheGame; + +// Set new game state + +void game_state(GameState state) +{ + // Set new state + TheGame.state = state; + + // Clear status line + + cwin_fill_rect(&cws, 0, 0, 40, 1, ' ', 1); + + switch (state) + { + case GS_READY: + // Start play in one second + TheGame.count = 60; + TheGame.step = 0; + + // Clear the board + board_init(); + board_init_main(&cwm); + break; + + case GS_PLAYER_MOVE: + // Place selection sprite in center + TheGame.posx = 3; + column_select_anim(&cwm, 3, 3, 7); + cwin_putat_string(&cws, 0, 0, P"PLAYER MOVE", 7); + break; + + case GS_COMPUTER_MOVE: + cwin_putat_string(&cws, 0, 0, P"COMPUTER MOVE", 2); + break; + + case GS_PLAYER_WIN: + cwin_putat_string(&cws, 0, 0, P"PLAYER WINS", 7); + + // Continue in 4 seconds + TheGame.count = 240; + break; + + case GS_COMPUTER_WIN: + cwin_putat_string(&cws, 0, 0, P"COMPUTER WINS", 2); + + // Continue in 4 seconds + TheGame.count = 240; + break; + + case GS_GAME_OVER: + cwin_putat_string(&cws, 0, 0, P"NO MORE MOVES", 1); + + // Continue in 4 seconds + TheGame.count = 240; + break; + } +} + +// Main game loop + +void game_loop() +{ + char bx = 0xff, cx; + + switch (TheGame.state) + { + case GS_READY: + if (!--TheGame.count) + game_state(GS_PLAYER_MOVE); + break; + + case GS_PLAYER_MOVE: + // Current selection + cx = TheGame.posx; + + // Check for key pressed + if (kbhit()) + { + // Get key + char ch = getch(); + + // Numbers 1 to 7 drop immediate + if (ch >= '1' && ch <= '7') + bx = cx = ch - '1'; + } + else + { + // Check Joystick + joy_poll(0); + + // Move cursor + if (joyx[0] < 0 && cx > 0) + cx--; + else if (joyx[0] > 0 && cx < 6) + cx++; + else if(joyb[0]) + bx = cx; + } + + // Move selection piece + column_select_anim(&cwm, TheGame.posx, cx, 7); + TheGame.posx = cx; + + if (bx != 0xff) + { + // Drop piece into board + if (board_drop(bx, true)) + { + // Remember move + TheGame.moves[TheGame.step++] = bx; + + // Play drop animation + item_drop_anim(&cwm, bx, board.ffree[bx], 7); + + // Show new state of the board + board_draw_main(&cwm); + spr_show(0, false); + + // Check for win condition + if (board_eval(true) == 32000) + game_state(GS_PLAYER_WIN); + else + game_state(GS_COMPUTER_MOVE); + } + } + break; + + case GS_COMPUTER_MOVE: + // Check for opening move or calculate next move + bx = board_opening(TheGame.step, TheGame.moves); + if (bx == 0xff) + bx = board_check(0, -32767, 32767, false); + + // Drop piece into board + if (board_drop(bx, false)) + { + // Play drop animation + item_drop_anim(&cwm, bx, board.ffree[bx], 2); + + // Show new state of the board + board_draw_main(&cwm); + spr_show(0, false); + + // Check for loss or draw condition + if (board_eval(false) == 32000) + game_state(GS_COMPUTER_WIN); + else if (TheGame.step == 21) + game_state(GS_GAME_OVER); + else + game_state(GS_PLAYER_MOVE); + } + break; + + case GS_PLAYER_WIN: + case GS_COMPUTER_WIN: + case GS_GAME_OVER: + if (!--TheGame.count) + game_state(GS_READY); + break; + } +} + + +int main(void) +{ + mmap_trampoline(); + + // Install character set + mmap_set(MMAP_RAM); + memcpy(Font, charset, 2048); + memcpy(Sprites, spriteset, 256); + mmap_set(MMAP_NO_BASIC); + + // Switch screen + vic_setmode(VICM_TEXT_MC, Screen, Font); + + spr_init(Screen); + + // Change colors + vic.color_border = VCOL_BLUE; + vic.color_back = VCOL_BLACK; + vic.color_back1 = VCOL_BLUE; + vic.color_back2 = VCOL_WHITE; + + vic.spr_mcolor0 = VCOL_DARK_GREY; + vic.spr_mcolor1 = VCOL_WHITE; + + // Clear screen + memset(Screen, 96, 1000); + memset(Color, 15, 1000); + + // Prepare char wins + cwin_init(&cws, Screen, 5, 0, 30, 1); + cwin_init(&cwm, Screen, 2, 3, 28, 19); + cwin_init(&cw, Screen, 30, 6, 8, 7); + cwin_init(&cwt, Screen, 30, 15, 8, 7); + + // Init AI + ai_init(); + + game_state(GS_READY); + + for(;;) + { + game_loop(); + vic_waitFrame(); + } + + + return 0; +} diff --git a/samples/games/make.bat b/samples/games/make.bat index cf03125..d5d2866 100644 --- a/samples/games/make.bat +++ b/samples/games/make.bat @@ -3,3 +3,4 @@ call ..\..\bin\oscar64 -n lander.c call ..\..\bin\oscar64 -n maze3d.c call ..\..\bin\oscar64 -n -O3 missile.c call ..\..\bin\oscar64 -n breakout.c +call ..\..\bin\oscar64 -n connectfour.c diff --git a/samples/memmap/easyflash.crt b/samples/memmap/easyflash.crt index c1bca42a040e5e81ef1e7ffea9c516b3e48e2ac4..bb3b6d1c0a3d10a42bc0d29778c48cfcec5a1a23 100644 GIT binary patch delta 497 zcmZ3`#J-@3eL}O+q*lq5Jgrg|D|ue>eBfWTl3@YEM9C8uem|2sb>W-E%DFEmO5xmHfzJeyI9ku8F!m1$?>a?(AGc@5eyCOHM#tMp)7cfm! zUdeM}qVh@y5YcdAqOw9A_ez*Q6*ez+_-{{yvcnBTDSLc?7-id;85xOEr|d|aI_E~l GyY>J8mdEb^ delta 498 zcmZ3`#J-@3eL}O+eBfWTl3@YEM9C8uem|2sb>W-E%6Ts*Ph7f_ zaq{GNMgyKHt-LFFTKN>5xmHfuJeyI9ku8#^m1$?xd7{Ts;fXHKoPGXH&SBAK4BI@1WwZ3=00koj z#VHGzCMvJwIWbXrr2~j)I5AOKp^kec%%h5%7d!m7Cqmib2BMTbK0u7J?aYjfM5$AD MBu<@kBja6r0HwgkegFUf diff --git a/samples/resources/connect4chars.bin b/samples/resources/connect4chars.bin new file mode 100644 index 0000000000000000000000000000000000000000..0187af09d1c451611fb12d461baffddb98829889 GIT binary patch literal 2048 zcmeHH&1xe@5UyAtA3Rt~Akc@fdIS&b#fN4mr(AvT>bQ8YpmSJbeA@NF%nv?zeKN)y zNCho(&l@C{NHjqh7UmVah|s67qYP#>Fm{Ei9zl{ro*-ZM(5j!V@2k>C+8iIphZ>YN zi7^JwjNuL1#BppioE{ltj!q$oan>kHs8}G)_tQPk-Ca`B;Owj#^dp;^vxKI(MlV` zewF}9KTu(ufDRcNf-l3f@UR8Vx&jTLw3LJ=VVZ_dU=5}L5FT+ELxzk2^*q-7GX;kx zP@(x2w9&iYnF#uUlDt5LVW;IViQO zluAIGejxfy|7-5Qzx~vt{XQh|S)Xlf;xo=qOdyiAF}(SF&T;O6w?9?5t-?4|+d|I* zPE^nj)QRvYP(ajHRW+Rwd38k5U@+L|O*^bLZ}j|{POGYF*V(_&MsI6tu=6X8MlbgF z5BBy(BOu0T^wn?o_kTX@rGAN%h#nvOO7{*9_Fs%f-v8t1@u6w%fB$|odYPta`f~J5 z2LRaE+S=Ix_(@U$AiJyDb|>up!FYkAphe(VDb98KugYIj)-)Q$eSz{ z1^X{<5b@@M@}jsXio81uj$b2MLiG2nV_5oF*L8g}C%n11y0}3q1UXIKX747G3E^d) zUn7!5h84a->an>Lo9|gHY=f3^sq2_W^8MjxGMT)~-cAV7I*Z7DmOyz^yRyj1mJCbv zALV)ZzDJgI)t1OvDM_EoEGs|xw}gn8hDdnChX@FW zB(HX5S-P4^SJ$;GiO_=O63*pZQQ$a0d1rBqs1>1P@%obI;N09fmy^Y&7X8>Pxc_GP z5woVDyf|;zwpg5VevY+Bwurp>oN=6c;O$RWE?rraZYlIE;L6oa?XHB!`9$Df?B~TS z%N8@4gP(=>|NCI@1sQKymNM45u8C+k;1=AXT1kuZ!H|d|0YEgonlF~Cwp}e3^Q$3y zx=1S^o9B7X`aQvImSwZW=kEO}3L;un3+J@Tt<9Atnm$$bB+4QbjwU3^VXwbpagUsY_=+s8hvu7VZ@Z(jt@;Adae9_w+45M`I zch2422_I2aRh8Jj;O1uM?`A7`qSwbow{uSN{FI_-*kRRSE$|;o0lI1WD7VDiTvqCJ JeX_ld{RR4+-VXo( literal 0 HcmV?d00001 diff --git a/samples/resources/connect4sprites.bin b/samples/resources/connect4sprites.bin new file mode 100644 index 0000000000000000000000000000000000000000..e8678750a9e44cc47c5d9da7e160fbeaedf169e8 GIT binary patch literal 1024 zcmZQzpa`@w{9<5U)xfuE0pF?#R;yN6ty=PG)v8siR-%$CSAmh%swG;hmT;|_z_w}v c<0=P+P=+RoY-eC-9o0_}7!5zlz>p6=054A|#Q*>R literal 0 HcmV?d00001