Add constant address calculation for struct members
This commit is contained in:
parent
a52fb469b6
commit
fdb112a037
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -34,6 +34,7 @@ enum DecType
|
|||
DT_CONST_DATA,
|
||||
DT_CONST_STRUCT,
|
||||
DT_CONST_POINTER,
|
||||
DT_CONST_REFERENCE,
|
||||
DT_CONST_ASSEMBLER,
|
||||
|
||||
DT_VARIABLE,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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:"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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.
Loading…
Reference in New Issue