Add constant address calculation for struct members

This commit is contained in:
drmortalwombat 2022-02-14 20:13:30 +01:00
parent a52fb469b6
commit fdb112a037
19 changed files with 1314 additions and 58 deletions

View File

@ -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

37
autotest/ptrinittest.c Normal file
View File

@ -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;
}

View File

@ -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,
};

View File

@ -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)

View File

@ -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)

View File

@ -34,6 +34,7 @@ enum DecType
DT_CONST_DATA,
DT_CONST_STRUCT,
DT_CONST_POINTER,
DT_CONST_REFERENCE,
DT_CONST_ASSEMBLER,
DT_VARIABLE,

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -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"

View File

@ -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:"

View File

@ -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

819
samples/games/connectfour.c Normal file
View File

@ -0,0 +1,819 @@
#include <c64/joystick.h>
#include <c64/vic.h>
#include <c64/sprites.h>
#include <c64/memmap.h>
#include <c64/rasterirq.h>
#include <c64/sid.h>
#include <c64/charwin.h>
#include <c64/types.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
// 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;
}

View File

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.