Order inter instructions into eval trains and sequences, optimize copyloops for size
This commit is contained in:
parent
a4fa4cd482
commit
dc0951ee9f
|
@ -163,6 +163,12 @@ exit /b %errorlevel%
|
|||
..\release\oscar64 -e -O0 -n %~1
|
||||
@if %errorlevel% neq 0 goto :error
|
||||
|
||||
..\release\oscar64 -e -Os %~1
|
||||
@if %errorlevel% neq 0 goto :error
|
||||
|
||||
..\release\oscar64 -e -Os -n %~1
|
||||
@if %errorlevel% neq 0 goto :error
|
||||
|
||||
..\release\oscar64 -e -O3 %~1
|
||||
@if %errorlevel% neq 0 goto :error
|
||||
|
||||
|
@ -181,6 +187,9 @@ exit /b %errorlevel%
|
|||
..\release\oscar64 -e -O0 %~1
|
||||
@if %errorlevel% neq 0 goto :error
|
||||
|
||||
..\release\oscar64 -e -Os %~1
|
||||
@if %errorlevel% neq 0 goto :error
|
||||
|
||||
..\release\oscar64 -e -O3 %~1
|
||||
@if %errorlevel% neq 0 goto :error
|
||||
|
||||
|
|
|
@ -412,6 +412,30 @@ static bool SameMem(const InterOperand& op1, const InterOperand& op2)
|
|||
}
|
||||
}
|
||||
|
||||
static bool SameMemRegion(const InterOperand& op1, const InterOperand& op2)
|
||||
{
|
||||
if (op1.mMemory != op2.mMemory)
|
||||
return false;
|
||||
|
||||
switch (op1.mMemory)
|
||||
{
|
||||
case IM_LOCAL:
|
||||
case IM_FPARAM:
|
||||
case IM_PARAM:
|
||||
case IM_FRAME:
|
||||
case IM_FFRAME:
|
||||
return true;
|
||||
case IM_ABSOLUTE:
|
||||
return true;
|
||||
case IM_GLOBAL:
|
||||
return op1.mLinkerObject == op2.mLinkerObject;
|
||||
case IM_INDIRECT:
|
||||
return op1.mTemp == op2.mTemp;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// returns true if op2 is part of op1
|
||||
static bool SameMemSegment(const InterOperand& op1, const InterOperand& op2)
|
||||
{
|
||||
|
@ -8426,7 +8450,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_LEA && (mInstructions[i]->mSrc[0].mTemp < 0 || mInstructions[i]->mSrc[1].mTemp < 0))
|
||||
{
|
||||
InterInstruction* ins(mInstructions[i]);
|
||||
int j = i;
|
||||
|
@ -8747,6 +8771,33 @@ void InterCodeBasicBlock::PeepholeOptimization(void)
|
|||
|
||||
} while (changed);
|
||||
|
||||
// sort stores up
|
||||
|
||||
do
|
||||
{
|
||||
changed = false;
|
||||
|
||||
for (int i = 0; i + 1 < mInstructions.Size(); i++)
|
||||
{
|
||||
if (mInstructions[i + 0]->mCode == IC_STORE && mInstructions[i + 1]->mCode == IC_STORE &&
|
||||
!mInstructions[i + 0]->mVolatile && !mInstructions[i + 1]->mVolatile &&
|
||||
// !CollidingMem(mInstructions[i + 0]->mSrc[1], mInstructions[i + 1]->mSrc[1]) &&
|
||||
SameMemRegion(mInstructions[i + 0]->mSrc[1], mInstructions[i + 1]->mSrc[1]) &&
|
||||
|
||||
(mInstructions[i + 0]->mSrc[1].mVarIndex > mInstructions[i + 1]->mSrc[1].mVarIndex ||
|
||||
mInstructions[i + 0]->mSrc[1].mVarIndex == mInstructions[i + 1]->mSrc[1].mVarIndex &&
|
||||
mInstructions[i + 0]->mSrc[1].mIntConst > mInstructions[i + 1]->mSrc[1].mIntConst))
|
||||
{
|
||||
InterInstruction* ins = mInstructions[i + 1];
|
||||
mInstructions[i + 1] = mInstructions[i + 0];
|
||||
mInstructions[i + 0] = ins;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
} while (changed);
|
||||
|
||||
|
||||
if (mTrueJump) mTrueJump->PeepholeOptimization();
|
||||
if (mFalseJump) mFalseJump->PeepholeOptimization();
|
||||
}
|
||||
|
@ -9746,6 +9797,21 @@ void InterCodeProcedure::Close(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
do {
|
||||
TempForwarding();
|
||||
} while (GlobalConstantPropagation());
|
||||
|
||||
ResetVisited();
|
||||
mEntryBlock->PeepholeOptimization();
|
||||
|
||||
TempForwarding();
|
||||
RemoveUnusedInstructions();
|
||||
|
||||
DisassembleDebug("Peephole optimized");
|
||||
|
||||
#endif
|
||||
|
||||
MapVariables();
|
||||
|
||||
DisassembleDebug("mapped variabled");
|
||||
|
|
|
@ -5124,8 +5124,56 @@ void NativeCodeBasicBlock::LoadValue(InterCodeProcedure* proc, const InterInstru
|
|||
|
||||
NativeCodeBasicBlock * NativeCodeBasicBlock::CopyValue(InterCodeProcedure* proc, const InterInstruction * ins, NativeCodeProcedure* nproc)
|
||||
{
|
||||
int size = ins->mConst.mOperandSize;
|
||||
int msize = 4;
|
||||
|
||||
if (nproc->mGenerator->mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL)
|
||||
msize = 8;
|
||||
else if (nproc->mGenerator->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE)
|
||||
msize = 2;
|
||||
#if 1
|
||||
if (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp < 0)
|
||||
{
|
||||
if (ins->mSrc[0].mMemory == IM_GLOBAL && ins->mSrc[1].mMemory == IM_FRAME)
|
||||
{
|
||||
int index = ins->mSrc[1].mVarIndex + ins->mSrc[1].mIntConst + 2;
|
||||
|
||||
int areg = BC_REG_STACK;
|
||||
CheckFrameIndex(areg, index, size);
|
||||
|
||||
if (size <= msize)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
mIns.Push(NativeCodeInstruction(ASMIT_LDY, ASMIM_IMMEDIATE, index + i));
|
||||
mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ABSOLUTE, ins->mSrc[0].mIntConst + i, ins->mSrc[0].mLinkerObject));
|
||||
mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_INDIRECT_Y, areg));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
else
|
||||
{
|
||||
NativeCodeBasicBlock* lblock = nproc->AllocateBlock();
|
||||
NativeCodeBasicBlock* eblock = nproc->AllocateBlock();
|
||||
|
||||
mIns.Push(NativeCodeInstruction(ASMIT_LDY, ASMIM_IMMEDIATE, index));
|
||||
this->Close(lblock, nullptr, ASMIT_JMP);
|
||||
lblock->mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ABSOLUTE_Y, ins->mSrc[0].mIntConst - index, ins->mSrc[0].mLinkerObject));
|
||||
lblock->mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_INDIRECT_Y, areg));
|
||||
lblock->mIns.Push(NativeCodeInstruction(ASMIT_INY, ASMIM_IMPLIED));
|
||||
lblock->mIns.Push(NativeCodeInstruction(ASMIT_CPY, ASMIM_IMMEDIATE, (index + size) & 255));
|
||||
lblock->Close(lblock, eblock, ASMIT_BNE);
|
||||
|
||||
return eblock;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int sreg, dreg;
|
||||
|
||||
|
||||
if (ins->mSrc[0].mTemp < 0)
|
||||
{
|
||||
if (ins->mSrc[0].mMemory == IM_GLOBAL)
|
||||
|
@ -5251,14 +5299,6 @@ NativeCodeBasicBlock * NativeCodeBasicBlock::CopyValue(InterCodeProcedure* proc,
|
|||
dreg = BC_REG_TMP + proc->mTempOffset[ins->mSrc[1].mTemp];
|
||||
}
|
||||
|
||||
int size = ins->mConst.mOperandSize;
|
||||
int msize = 4;
|
||||
|
||||
if (nproc->mGenerator->mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL)
|
||||
msize = 8;
|
||||
else if (nproc->mGenerator->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE)
|
||||
msize = 2;
|
||||
|
||||
if (size <= msize)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
|
@ -14055,10 +14095,160 @@ bool NativeCodeBasicBlock::OptimizeSelect(NativeCodeProcedure* proc)
|
|||
return changed;
|
||||
}
|
||||
|
||||
static bool CheckBlockCopySequence(const GrowingArray<NativeCodeInstruction>& ins, int si, int n)
|
||||
{
|
||||
if (si + 2 * n <= ins.Size() &&
|
||||
ins[si + 0].mType == ASMIT_LDA && (ins[si + 0].mMode == ASMIM_ZERO_PAGE || ins[si + 0].mMode == ASMIM_ABSOLUTE) &&
|
||||
ins[si + 1].mType == ASMIT_STA && (ins[si + 1].mMode == ASMIM_ZERO_PAGE || ins[si + 1].mMode == ASMIM_ABSOLUTE))
|
||||
{
|
||||
for (int i = 1; i < n; i++)
|
||||
{
|
||||
if (!(ins[si + 2 * i + 0].mType == ASMIT_LDA && ins[si + 2 * i + 0].mMode == ins[si + 0].mMode && ins[si + 2 * i + 0].mAddress == ins[si + 0].mAddress + i &&
|
||||
ins[si + 2 * i + 1].mType == ASMIT_STA && ins[si + 2 * i + 1].mMode == ins[si + 1].mMode && ins[si + 2 * i + 1].mAddress == ins[si + 1].mAddress + i))
|
||||
return false;
|
||||
}
|
||||
if (ins[si + 2 * n - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NativeCodeBasicBlock::BlockSizeCopyReduction(NativeCodeProcedure* proc, int& si, int& di)
|
||||
{
|
||||
if ((proc->mGenerator->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE))
|
||||
{
|
||||
if (si + 1 < mIns.Size() &&
|
||||
mIns[si + 0].mType == ASMIT_LDA && (mIns[si + 0].mMode == ASMIM_ZERO_PAGE || mIns[si + 0].mMode == ASMIM_ABSOLUTE) &&
|
||||
mIns[si + 1].mType == ASMIT_STA && (mIns[si + 1].mMode == ASMIM_ZERO_PAGE || mIns[si + 1].mMode == ASMIM_ABSOLUTE))
|
||||
{
|
||||
int i = 1;
|
||||
while (si + 2 * i + 1 < mIns.Size() &&
|
||||
mIns[si + 2 * i + 0].mType == ASMIT_LDA && mIns[si + 2 * i + 0].mMode == mIns[si + 0].mMode && mIns[si + 2 * i + 0].mAddress == mIns[si + 0].mAddress + i &&
|
||||
mIns[si + 2 * i + 1].mType == ASMIT_STA && mIns[si + 2 * i + 1].mMode == mIns[si + 1].mMode && mIns[si + 2 * i + 1].mAddress == mIns[si + 1].mAddress + i &&
|
||||
!(mIns[si + 2 * i + 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i > 2)
|
||||
{
|
||||
if (!(mIns[si + 0].mLive & LIVE_CPU_REG_X))
|
||||
{
|
||||
int k = 1;
|
||||
while (CheckBlockCopySequence(mIns, si + 2 * k * i, i))
|
||||
k++;
|
||||
|
||||
|
||||
int sz = 3 + 4 * k;
|
||||
for (int j = 0; j < k; j++)
|
||||
{
|
||||
NativeCodeInstruction lins = mIns[si + 2 * i * j + 0];
|
||||
NativeCodeInstruction sins = mIns[si + 2 * i * j + 1];
|
||||
|
||||
if (lins.mMode == ASMIM_ZERO_PAGE)
|
||||
lins.mMode = ASMIM_ZERO_PAGE_X;
|
||||
else
|
||||
{
|
||||
lins.mMode = ASMIM_ABSOLUTE_X;
|
||||
sz++;
|
||||
}
|
||||
if (sins.mMode == ASMIM_ZERO_PAGE)
|
||||
sins.mMode = ASMIM_ZERO_PAGE_X;
|
||||
else
|
||||
{
|
||||
sins.mMode = ASMIM_ABSOLUTE_X;
|
||||
sz++;
|
||||
}
|
||||
|
||||
if (j == 0)
|
||||
mIns[di++] = NativeCodeInstruction(ASMIT_LDX, ASMIM_IMMEDIATE, i - 1);
|
||||
|
||||
mIns[di++] = lins;
|
||||
mIns[di++] = sins;
|
||||
}
|
||||
|
||||
mIns[di++] = NativeCodeInstruction(ASMIT_DEX, ASMIM_IMPLIED);
|
||||
mIns[di++] = NativeCodeInstruction(ASMIT_BPL, ASMIM_RELATIVE, -sz);
|
||||
|
||||
si += 2 * i * k;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (si + 2 < mIns.Size() &&
|
||||
mIns[si + 0].mType == ASMIT_LDA && (mIns[si + 0].mMode == ASMIM_ZERO_PAGE || mIns[si + 0].mMode == ASMIM_ABSOLUTE) &&
|
||||
mIns[si + 1].mType == ASMIT_STA && (mIns[si + 1].mMode == ASMIM_ZERO_PAGE || mIns[si + 1].mMode == ASMIM_ABSOLUTE) &&
|
||||
mIns[si + 1].mType == ASMIT_STA && (mIns[si + 1].mMode == ASMIM_ZERO_PAGE || mIns[si + 1].mMode == ASMIM_ABSOLUTE))
|
||||
{
|
||||
int i = 1;
|
||||
while (si + 3 * i + 2 < mIns.Size() &&
|
||||
mIns[si + 3 * i + 0].mType == ASMIT_LDA && mIns[si + 3 * i + 0].mMode == mIns[si + 0].mMode && mIns[si + 3 * i + 0].mAddress == mIns[si + 0].mAddress + i &&
|
||||
mIns[si + 3 * i + 1].mType == ASMIT_STA && mIns[si + 3 * i + 1].mMode == mIns[si + 1].mMode && mIns[si + 3 * i + 1].mAddress == mIns[si + 1].mAddress + i &&
|
||||
mIns[si + 3 * i + 2].mType == ASMIT_STA && mIns[si + 3 * i + 2].mMode == mIns[si + 2].mMode && mIns[si + 3 * i + 2].mAddress == mIns[si + 2].mAddress + i &&
|
||||
!(mIns[si + 3 * i + 2].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i > 2)
|
||||
{
|
||||
if (!(mIns[si + 0].mLive & LIVE_CPU_REG_X))
|
||||
{
|
||||
NativeCodeInstruction lins = mIns[si + 0];
|
||||
NativeCodeInstruction sins0 = mIns[si + 1];
|
||||
NativeCodeInstruction sins1 = mIns[si + 2];
|
||||
|
||||
int sz = 9;
|
||||
if (lins.mMode == ASMIM_ZERO_PAGE)
|
||||
lins.mMode = ASMIM_ZERO_PAGE_X;
|
||||
else
|
||||
{
|
||||
lins.mMode = ASMIM_ABSOLUTE_X;
|
||||
sz++;
|
||||
}
|
||||
if (sins0.mMode == ASMIM_ZERO_PAGE)
|
||||
sins0.mMode = ASMIM_ZERO_PAGE_X;
|
||||
else
|
||||
{
|
||||
sins0.mMode = ASMIM_ABSOLUTE_X;
|
||||
sz++;
|
||||
}
|
||||
if (sins1.mMode == ASMIM_ZERO_PAGE)
|
||||
sins1.mMode = ASMIM_ZERO_PAGE_X;
|
||||
else
|
||||
{
|
||||
sins1.mMode = ASMIM_ABSOLUTE_X;
|
||||
sz++;
|
||||
}
|
||||
|
||||
mIns[di++] = NativeCodeInstruction(ASMIT_LDX, ASMIM_IMMEDIATE, i - 1);
|
||||
mIns[di++] = lins;
|
||||
mIns[di++] = sins0;
|
||||
mIns[di++] = sins1;
|
||||
mIns[di++] = NativeCodeInstruction(ASMIT_DEX, ASMIM_IMPLIED);
|
||||
mIns[di++] = NativeCodeInstruction(ASMIT_BPL, ASMIM_RELATIVE, -sz);
|
||||
|
||||
si += 3 * i;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Size reduction violating various assumptions such as no branches in basic blocks
|
||||
// must be last step before actual assembly
|
||||
|
||||
void NativeCodeBasicBlock::BlockSizeReduction(void)
|
||||
void NativeCodeBasicBlock::BlockSizeReduction(NativeCodeProcedure* proc)
|
||||
{
|
||||
if (!mVisited)
|
||||
{
|
||||
|
@ -14412,6 +14602,10 @@ void NativeCodeBasicBlock::BlockSizeReduction(void)
|
|||
{
|
||||
mIns[j++] = mIns[i++];
|
||||
i++;
|
||||
}
|
||||
else if (BlockSizeCopyReduction(proc, i, j))
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
mIns[j++] = mIns[i++];
|
||||
|
@ -14496,6 +14690,8 @@ void NativeCodeBasicBlock::BlockSizeReduction(void)
|
|||
ximm = false;
|
||||
else if (mIns[i].mType == ASMIT_JSR)
|
||||
yimm = ximm = false;
|
||||
else if (mIns[i].mMode == ASMIM_RELATIVE && mIns[i].mAddress < 0)
|
||||
yimm = ximm = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -14704,9 +14900,9 @@ void NativeCodeBasicBlock::BlockSizeReduction(void)
|
|||
|
||||
#endif
|
||||
if (mTrueJump)
|
||||
mTrueJump->BlockSizeReduction();
|
||||
mTrueJump->BlockSizeReduction(proc);
|
||||
if (mFalseJump)
|
||||
mFalseJump->BlockSizeReduction();
|
||||
mFalseJump->BlockSizeReduction(proc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18622,8 +18818,6 @@ void NativeCodeProcedure::Compile(InterCodeProcedure* proc)
|
|||
|
||||
mExitBlock->mIns.Pop();
|
||||
|
||||
CompressTemporaries();
|
||||
|
||||
int frameSpace = tempSave;
|
||||
|
||||
tempSave = proc->mTempSize > 16 ? proc->mTempSize - 16 : 0;
|
||||
|
@ -19201,9 +19395,11 @@ void NativeCodeProcedure::Optimize(void)
|
|||
cnt++;
|
||||
} while (changed);
|
||||
|
||||
CompressTemporaries();
|
||||
|
||||
#if 1
|
||||
ResetVisited();
|
||||
mEntryBlock->BlockSizeReduction();
|
||||
mEntryBlock->BlockSizeReduction(this);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -158,7 +158,8 @@ public:
|
|||
|
||||
bool RemoveNops(void);
|
||||
bool PeepHoleOptimizer(NativeCodeProcedure* proc, int pass);
|
||||
void BlockSizeReduction(void);
|
||||
void BlockSizeReduction(NativeCodeProcedure* proc);
|
||||
bool BlockSizeCopyReduction(NativeCodeProcedure* proc, int & si, int & di);
|
||||
|
||||
bool OptimizeSimpleLoopInvariant(NativeCodeProcedure* proc);
|
||||
bool OptimizeSimpleLoopInvariant(NativeCodeProcedure* proc, NativeCodeBasicBlock * prevBlock, NativeCodeBasicBlock* exitBlock);
|
||||
|
|
|
@ -8,84 +8,115 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Graphics areas in bank 3
|
||||
byte * const Screen = (byte *)0xc800;
|
||||
byte * const Font = (byte *)0xe000;
|
||||
byte * const TextFont = (byte *)0xe800;
|
||||
byte * const Color = (byte *)0xd800;
|
||||
byte * const Sprites = (byte *)0xd000;
|
||||
|
||||
// Character set
|
||||
// Character set for scrolling background
|
||||
char charset[2048] = {
|
||||
#embed "../resources/hscrollshmupchars.bin"
|
||||
};
|
||||
|
||||
// Character set for status line
|
||||
char tcharset[2048] = {
|
||||
#embed "../resources/breakoutchars.bin"
|
||||
};
|
||||
|
||||
// Tileset for scrolling background
|
||||
char tileset[] = {
|
||||
#embed "../resources/hscrollshmuptiles.bin"
|
||||
};
|
||||
|
||||
// Tilemap for scrolling background
|
||||
char tilemap[128 * 5] = {
|
||||
#embed "../resources/hscrollshmupmap.bin"
|
||||
};
|
||||
|
||||
// Spriteset for player and enemies
|
||||
char spriteset[4096] = {
|
||||
#embed 4096 0 "../resources/hscrollshmupsprites.bin"
|
||||
};
|
||||
|
||||
// Converted tileset
|
||||
char xtileset[16][64];
|
||||
|
||||
// Converted tilemat
|
||||
char xtilemap[144 * 5];
|
||||
|
||||
// Horizontal position for the stars in the background
|
||||
char stars[24];
|
||||
|
||||
// Enemy/shot collision check table
|
||||
char xcollision[256];
|
||||
|
||||
// Align the table data to avoid page crossing when indexing
|
||||
#pragma align(xtileset, 64);
|
||||
#pragma align(xcollision, 256)
|
||||
|
||||
// Raster interrupts
|
||||
RIRQCode bottom, top;
|
||||
|
||||
// List of enemies
|
||||
struct Enemy
|
||||
{
|
||||
int px;
|
||||
byte py;
|
||||
sbyte dx;
|
||||
byte state, pad0, pad1, pad2;
|
||||
int px; // absolute horizontal position
|
||||
byte py; // absolute vertical position
|
||||
sbyte dx; // horizontal speed
|
||||
byte state; // state of the enemy
|
||||
byte pad0, pad1, pad2; // padding to align at 8 bytes
|
||||
} enemies[5];
|
||||
|
||||
// Shot data
|
||||
struct Shot
|
||||
{
|
||||
byte ty, x, ry, n;
|
||||
sbyte dx; // Direction of shot
|
||||
Shot * next; // Next shot in list
|
||||
} shots[18]; // Pre-allocated shots
|
||||
|
||||
Shot * freeShot;
|
||||
|
||||
|
||||
// Player data
|
||||
struct Player
|
||||
{
|
||||
int spx;
|
||||
sbyte vpx;
|
||||
sbyte ax;
|
||||
char aphase;
|
||||
char spy;
|
||||
char fdelay;
|
||||
int spx; // absolute screen position
|
||||
sbyte vpx; // horizontal velocity
|
||||
sbyte ax; // acceleration
|
||||
char aphase; // animation phase
|
||||
char spy; // vertical position
|
||||
char fdelay; // auto fire delay
|
||||
} player;
|
||||
|
||||
// Game state
|
||||
enum GameState
|
||||
{
|
||||
GS_START,
|
||||
GS_START, // Waiting for the game to start
|
||||
|
||||
GS_READY, // Getting ready
|
||||
|
||||
GS_PLAYING,
|
||||
GS_EXPLODING,
|
||||
GS_PLAYING, // Actually playing
|
||||
GS_EXPLODING, // Player ship is exploding
|
||||
|
||||
GS_GAME_OVER
|
||||
GS_GAME_OVER // Game is over
|
||||
};
|
||||
|
||||
|
||||
// Game data
|
||||
struct Game
|
||||
{
|
||||
GameState state;
|
||||
char count;
|
||||
char edelay;
|
||||
char ecount;
|
||||
char escore;
|
||||
char lives;
|
||||
GameState state; // Current game state
|
||||
char count; // Countdown for states that change after delay
|
||||
char edelay; // enemy wave delay
|
||||
char ecount; // number of live enemies
|
||||
char escore; // score not yet accumulated
|
||||
char lives; // number of player lives left
|
||||
} game;
|
||||
|
||||
|
||||
// Sound effect for a player shot
|
||||
SIDFX SIDFXFire[1] = {{
|
||||
8000, 1000,
|
||||
SID_CTRL_GATE | SID_CTRL_SAW,
|
||||
|
@ -95,6 +126,7 @@ SIDFX SIDFXFire[1] = {{
|
|||
4, 30
|
||||
}};
|
||||
|
||||
// Sound effect for enemy explosion
|
||||
SIDFX SIDFXExplosion[1] = {{
|
||||
1000, 1000,
|
||||
SID_CTRL_GATE | SID_CTRL_NOISE,
|
||||
|
@ -104,6 +136,7 @@ SIDFX SIDFXExplosion[1] = {{
|
|||
8, 40
|
||||
}};
|
||||
|
||||
// Sound effect for player explosion
|
||||
SIDFX SIDFXBigExplosion[3] = {
|
||||
{
|
||||
1000, 1000,
|
||||
|
@ -142,14 +175,19 @@ void status_init(void)
|
|||
Screen[i] = StatusText[i];
|
||||
}
|
||||
|
||||
// update the status line
|
||||
void status_update(void)
|
||||
{
|
||||
// Set number of lives
|
||||
Screen[38] = game.lives + '0';
|
||||
|
||||
// Increment the score from a given digit on
|
||||
if (game.escore)
|
||||
{
|
||||
char at, val = 1;
|
||||
|
||||
// Check for 100s, 10s and 1s and set digit to increment
|
||||
// appropriately
|
||||
if (game.escore >= 100)
|
||||
{
|
||||
game.escore -= 100;
|
||||
|
@ -216,12 +254,20 @@ void score_reset(void)
|
|||
// unpack tiles into a fast accessible format
|
||||
void tiles_unpack(void)
|
||||
{
|
||||
// The tileset in the loaded binary is organized as an array
|
||||
// of arrays of 16 characters. These loops reorganize the data
|
||||
// into 16 arrays of arrays of char. This gives direct 8bit
|
||||
// index access to each char of a given tile
|
||||
|
||||
for(char t=0; t<64; t++)
|
||||
{
|
||||
for(char i=0; i<16; i++)
|
||||
xtileset[i][t] = tileset[16 * t + i];
|
||||
}
|
||||
|
||||
// The left side of the tilemap is duplicated to avoid
|
||||
// overflow checks at the right border
|
||||
|
||||
for(char y=0; y<5; y++)
|
||||
{
|
||||
for(char x=0; x<144; x++)
|
||||
|
@ -230,6 +276,10 @@ void tiles_unpack(void)
|
|||
}
|
||||
}
|
||||
|
||||
// The shots are stored with row and column, this array allows for
|
||||
// easy check of collision with an enemy by subtracting the
|
||||
// character column of the enemy
|
||||
|
||||
for(char i=0; i<160; i+=40)
|
||||
{
|
||||
for(char j=0; j<3; j++)
|
||||
|
@ -237,35 +287,46 @@ void tiles_unpack(void)
|
|||
}
|
||||
}
|
||||
|
||||
// Draw the tiles of one tile row with no horizontal offset
|
||||
// into line dp
|
||||
|
||||
void tiles_draw0(char * dp, char * tm)
|
||||
{
|
||||
// Second, third and final target line
|
||||
char * ap = dp + 40;
|
||||
char * bp = dp + 80;
|
||||
char * cp = dp + 120;
|
||||
|
||||
// target column
|
||||
|
||||
char q = 0;
|
||||
for(char x=0; x<10; x++)
|
||||
{
|
||||
// Get tile
|
||||
char ti = tm[x];
|
||||
|
||||
// First tile column
|
||||
dp[ q] = xtileset[ 0][ti];
|
||||
ap[ q] = xtileset[ 4][ti];
|
||||
bp[ q] = xtileset[ 8][ti];
|
||||
cp[ q] = xtileset[12][ti];
|
||||
q++;
|
||||
|
||||
// Second tile column
|
||||
dp[ q] = xtileset[ 1][ti];
|
||||
ap[ q] = xtileset[ 5][ti];
|
||||
bp[ q] = xtileset[ 9][ti];
|
||||
cp[ q] = xtileset[13][ti];
|
||||
q++;
|
||||
|
||||
// Third tile column
|
||||
dp[ q] = xtileset[ 2][ti];
|
||||
ap[ q] = xtileset[ 6][ti];
|
||||
bp[ q] = xtileset[10][ti];
|
||||
cp[ q] = xtileset[14][ti];
|
||||
q++;
|
||||
|
||||
// Final tile column
|
||||
dp[ q] = xtileset[ 3][ti];
|
||||
ap[ q] = xtileset[ 7][ti];
|
||||
bp[ q] = xtileset[11][ti];
|
||||
|
@ -274,6 +335,9 @@ void tiles_draw0(char * dp, char * tm)
|
|||
}
|
||||
}
|
||||
|
||||
// Draw the tiles of one tile row with one char horizontal offset
|
||||
// into line dp
|
||||
|
||||
void tiles_draw3(char * dp, char * tm)
|
||||
{
|
||||
char ti = tm[0];
|
||||
|
@ -285,14 +349,17 @@ void tiles_draw3(char * dp, char * tm)
|
|||
char q = 0;
|
||||
for(char x=1; x<11; x++)
|
||||
{
|
||||
// Start with the fourth column of the previous tile
|
||||
dp[ q] = xtileset[ 3][ti];
|
||||
ap[ q] = xtileset[ 7][ti];
|
||||
bp[ q] = xtileset[11][ti];
|
||||
cp[ q] = xtileset[15][ti];
|
||||
q++;
|
||||
|
||||
// Advance to next tile
|
||||
ti = tm[x];
|
||||
|
||||
// Now three columns of the new tile
|
||||
dp[ q] = xtileset[ 0][ti];
|
||||
ap[ q] = xtileset[ 4][ti];
|
||||
bp[ q] = xtileset[ 8][ti];
|
||||
|
@ -391,64 +458,73 @@ void tiles_draw1(char * dp, char * tm)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
struct Shot
|
||||
{
|
||||
byte ty, x, ry, n;
|
||||
sbyte dx;
|
||||
Shot * next;
|
||||
} shots[18];
|
||||
|
||||
Shot * freeShot;
|
||||
|
||||
// Initialize the list of shots
|
||||
void shot_init(void)
|
||||
{
|
||||
// First shot is just a list head and doubles
|
||||
// as the last shot in the list as well, so a
|
||||
// circular list
|
||||
|
||||
shots[0].next = shots;
|
||||
shots[0].ty = 6;
|
||||
|
||||
// Build list of free shots
|
||||
freeShot = shots + 1;
|
||||
for(char i=1; i<17; i++)
|
||||
shots[i].next = shots + i + 1;
|
||||
shots[17].next = nullptr;
|
||||
}
|
||||
|
||||
// Draw one shot
|
||||
inline void shot_draw(char * dp, char i, char xp, char yp)
|
||||
{
|
||||
// Character below the shot
|
||||
char c = dp[xp];
|
||||
|
||||
// We know there are only 20 shots
|
||||
__assume(i < 20);
|
||||
|
||||
// Character code for background with overlayed shot
|
||||
dp[xp] = i | 0xe0;
|
||||
|
||||
// Source and target character code
|
||||
char * fsp = Font + 8 * c;
|
||||
char * fdp = (Font + 0xe0 * 8) + 8 * i;
|
||||
|
||||
// Copy the background character into the shot character
|
||||
fdp[0] = fsp[0]; fdp[1] = fsp[1]; fdp[2] = fsp[2]; fdp[3] = fsp[3];
|
||||
fdp[4] = fsp[4]; fdp[5] = fsp[5]; fdp[6] = fsp[6]; fdp[7] = fsp[7];
|
||||
|
||||
// Put the line
|
||||
fdp[yp] = 0x00;
|
||||
}
|
||||
|
||||
// Add a shot to the active list
|
||||
void shot_add(int sx, char sy, sbyte dx)
|
||||
{
|
||||
// Actual screen position
|
||||
char py = sy - 14;
|
||||
char gy = py >> 5;
|
||||
char ey = (py >> 3) & 3;
|
||||
char ry = py & 7;
|
||||
|
||||
// Get a new shot structure from the free list
|
||||
Shot * s = freeShot;
|
||||
freeShot = s->next;
|
||||
|
||||
// Insert it into the sorted list of active shots
|
||||
Shot * p = shots;
|
||||
while (p->next->ty < gy)
|
||||
p = p->next;
|
||||
s->next = p->next;
|
||||
p->next = s;
|
||||
|
||||
// Init structure
|
||||
s->ty = gy;
|
||||
s->ry = ry;
|
||||
s->dx = dx;
|
||||
|
||||
// Horizontal start postion based on player sprite
|
||||
if (dx < 0)
|
||||
{
|
||||
char x = (sx) >> 3;
|
||||
|
@ -463,33 +539,47 @@ void shot_add(int sx, char sy, sbyte dx)
|
|||
}
|
||||
}
|
||||
|
||||
// Draw the screen with tiles, stars and shots at the given
|
||||
// absolute pixel position
|
||||
void tiles_draw(unsigned x)
|
||||
{
|
||||
// vic.color_border++;
|
||||
|
||||
// Wait for the raster to be below the first tile row
|
||||
vic_waitTop();
|
||||
while (vic.raster < 106)
|
||||
;
|
||||
// vic.color_border--;
|
||||
|
||||
// Pixel scroll offset
|
||||
char xs = 7 - (x & 7);
|
||||
|
||||
// Update raster IRQ for next screen
|
||||
rirq_data(&top, 1, VIC_CTRL2_MCM | xs);
|
||||
rirq_data(&top, 2, ~(1 << xs));
|
||||
|
||||
// Character position
|
||||
x >>= 3;
|
||||
|
||||
// Tile position and tile offset
|
||||
char xl = x >> 2, xr = x & 3;
|
||||
char yl = 0;
|
||||
char ci = 0;
|
||||
|
||||
// First shot in list
|
||||
Shot * ps = shots;
|
||||
|
||||
// Loop over five rows of 4x4 tiles
|
||||
for(int iy=0; iy<5; iy++)
|
||||
{
|
||||
// Target position
|
||||
char * dp = Screen + 120 + 160 * iy;
|
||||
char * cp = Color + 120 + 160 * iy;
|
||||
|
||||
// Tile source position
|
||||
char * tp = xtilemap + xl + 144 * iy;
|
||||
|
||||
// Draw tiles with given char offset
|
||||
switch (xr)
|
||||
{
|
||||
case 0:
|
||||
|
@ -508,6 +598,9 @@ void tiles_draw(unsigned x)
|
|||
__assume(false);
|
||||
}
|
||||
|
||||
// Draw the four stars for the tile row, empty
|
||||
// space has char code zero
|
||||
|
||||
char k = stars[yl + 0] + 0;
|
||||
if (dp[k])
|
||||
cp[k] = 8;
|
||||
|
@ -544,17 +637,26 @@ void tiles_draw(unsigned x)
|
|||
dp[k] = 0xf8;
|
||||
}
|
||||
|
||||
// Loop over all shots in the current tile row
|
||||
Shot *ss = ps->next;
|
||||
|
||||
while (ss->ty == iy)
|
||||
{
|
||||
// Advance position
|
||||
ss->x += ss->dx;
|
||||
|
||||
// Decrement live
|
||||
ss->n--;
|
||||
|
||||
// Draw the shot
|
||||
shot_draw(dp, ci++, ss->x, ss->ry);
|
||||
|
||||
if (ss->n)
|
||||
ps = ss;
|
||||
else
|
||||
{
|
||||
// End of live, remove from the list
|
||||
|
||||
ps->next = ss->next;
|
||||
ss->next = freeShot;
|
||||
freeShot = ss;
|
||||
|
@ -562,41 +664,54 @@ void tiles_draw(unsigned x)
|
|||
ss = ps->next;
|
||||
}
|
||||
|
||||
// Next tile row
|
||||
yl += 4;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Intitialize player
|
||||
void player_init(void)
|
||||
{
|
||||
// Set up start position and direction
|
||||
player.vpx = 0;
|
||||
player.ax = 1;
|
||||
player.spy = 100;
|
||||
player.fdelay = 0;
|
||||
|
||||
// Show sprites
|
||||
spr_set(0, true, 160, 100, 64, VCOL_BLUE, true, false, false);
|
||||
spr_set(7, true, 160, 100, 64 + 16, VCOL_MED_GREY, true, false, false);
|
||||
|
||||
// Background has priority before shadow sprite
|
||||
vic.spr_priority = 0x80;
|
||||
}
|
||||
|
||||
// Use the joystick to control the player
|
||||
void player_control(void)
|
||||
{
|
||||
// Poll the joystick
|
||||
joy_poll(0);
|
||||
|
||||
// Change horizontal direction if joystick left or right
|
||||
if (joyx[0] != 0)
|
||||
player.ax = joyx[0];
|
||||
|
||||
// Change vertical position if joystick up or down
|
||||
player.spy += 2 * joyy[0];
|
||||
if (player.spy < 14)
|
||||
player.spy = 14;
|
||||
else if (player.spy > 14 + 159)
|
||||
player.spy = 14 + 159;
|
||||
|
||||
// Animate/accelerate based on requested direction
|
||||
if (player.ax > 0)
|
||||
{
|
||||
// Speed up until max speed
|
||||
if (player.vpx < 32)
|
||||
player.vpx++;
|
||||
|
||||
// Adapt animation phase for direction change
|
||||
if (player.aphase > 0 && player.aphase < 16)
|
||||
player.aphase--;
|
||||
else if (player.aphase >= 16 && player.aphase < 32)
|
||||
|
@ -614,40 +729,50 @@ void player_control(void)
|
|||
|
||||
}
|
||||
|
||||
// Modulus animation phase
|
||||
player.aphase &= 31;
|
||||
|
||||
// Set player sprite image based on animation phase
|
||||
spr_image(0, 64 + (player.aphase >> 1));
|
||||
spr_image(7, 80 + (player.aphase >> 1));
|
||||
|
||||
// Player position based on speed
|
||||
int px = 148 - 4 * player.vpx;
|
||||
|
||||
// Check for player shooting
|
||||
if (player.fdelay)
|
||||
player.fdelay--;
|
||||
else if (joyb[0])
|
||||
{
|
||||
if (player.aphase < 4 || player.aphase >= 29)
|
||||
{
|
||||
// Fire right
|
||||
shot_add(px, player.spy, 1);
|
||||
sidfx_play(0, SIDFXFire, 1);
|
||||
}
|
||||
else if (player.aphase >= 13 && player.aphase < 20)
|
||||
{
|
||||
// Fire left
|
||||
shot_add(px, player.spy, -1);
|
||||
sidfx_play(0, SIDFXFire, 1);
|
||||
}
|
||||
|
||||
// Delay next shot by six frames
|
||||
player.fdelay = 6;
|
||||
}
|
||||
|
||||
// Move player sprite
|
||||
spr_move(0, 24 + px, 50 + player.spy);
|
||||
spr_move(7, 32 + px, 58 + player.spy);
|
||||
}
|
||||
|
||||
// Advance absolute player/screen position
|
||||
void player_move()
|
||||
{
|
||||
player.spx += player.vpx >> 2;
|
||||
}
|
||||
|
||||
// Initialize game structure
|
||||
void game_init(void)
|
||||
{
|
||||
game.edelay = 80;
|
||||
|
@ -657,23 +782,31 @@ void game_init(void)
|
|||
player.spx = 40;
|
||||
}
|
||||
|
||||
// Move all active enemies
|
||||
void enemies_move(void)
|
||||
{
|
||||
// Loop over potential five enemies
|
||||
for(char i=0; i<5; i++)
|
||||
{
|
||||
// Check if enemy is active
|
||||
if (enemies[i].state)
|
||||
{
|
||||
// Advance position
|
||||
enemies[i].px += enemies[i].dx;
|
||||
|
||||
// Check for relative position to player
|
||||
int rx = enemies[i].px - player.spx;
|
||||
|
||||
if (rx < -192 || rx >= 480)
|
||||
{
|
||||
// Enemy is outside scope, remove it from active set
|
||||
enemies[i].state = 0;
|
||||
game.ecount--;
|
||||
spr_show(2 + i, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move enemy sprite
|
||||
spr_move(2 + i, rx + 24, enemies[i].py + 50);
|
||||
|
||||
if (enemies[i].state & 0x80)
|
||||
|
@ -682,11 +815,13 @@ void enemies_move(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
// Enemy is eploding
|
||||
spr_color(2 + i, VCOL_YELLOW);
|
||||
spr_image(2 + i, 127 - (enemies[i].state >> 2));
|
||||
enemies[i].state--;
|
||||
if (enemies[i].state == 0)
|
||||
{
|
||||
// Explosion complete, remove from active set
|
||||
spr_show(2 + i, false);
|
||||
game.ecount--;
|
||||
}
|
||||
|
@ -696,49 +831,75 @@ void enemies_move(void)
|
|||
}
|
||||
}
|
||||
|
||||
// Spwan one new enemy outside screen
|
||||
void enemies_spawn(void)
|
||||
{
|
||||
// Seed for enemy
|
||||
char u = rand();
|
||||
|
||||
// Index of enemy
|
||||
char e = game.ecount;
|
||||
|
||||
// We know there are only five, help the compiler
|
||||
// with the indexing
|
||||
__assume(e < 5);
|
||||
|
||||
// Speed of enemy
|
||||
sbyte v = 1 + (u & 3);
|
||||
|
||||
// Vertical position
|
||||
enemies[e].py = 29 + 32 * e;
|
||||
|
||||
// Speed and direction
|
||||
enemies[e].dx = player.vpx < 0 ? v : -v;
|
||||
|
||||
// Position left or right outside
|
||||
enemies[e].px = (player.vpx < 0 ? player.spx - 56 : player.spx + 320) + ((u >> 1) & 31);
|
||||
|
||||
// Initial status is alive
|
||||
enemies[e].state = 0x80;
|
||||
|
||||
// Set enemy sprite outside screen
|
||||
int rx = enemies[e].px - player.spx;
|
||||
|
||||
spr_set(2 + e, true, rx + 24, enemies[e].py + 50, player.vpx < 0 ? 97 : 96, VCOL_LT_BLUE, true, false, false);
|
||||
|
||||
// One more enemy ready
|
||||
game.ecount++;
|
||||
}
|
||||
|
||||
// Remove and reset all enemies
|
||||
void enemies_reset(void)
|
||||
{
|
||||
for(char i=0; i<5; i++)
|
||||
{
|
||||
// Hide sprite and clearout state
|
||||
spr_show(2 + i, false);
|
||||
enemies[i].state = 0x00;
|
||||
}
|
||||
|
||||
// Delay for next enemy to show up
|
||||
game.edelay = 80;
|
||||
game.ecount = 0;
|
||||
}
|
||||
|
||||
// Check collision player with enemy
|
||||
bool player_check(void)
|
||||
{
|
||||
// Index of enemy in players tile row
|
||||
char e = (player.spy - 14) >> 5;
|
||||
|
||||
// Is it active?
|
||||
if (e < 5 && (enemies[e].state & 0x80))
|
||||
{
|
||||
// Relative horizontal position
|
||||
int rx = enemies[e].px - player.spx;
|
||||
|
||||
rx -= 148 - 4 * player.vpx;
|
||||
|
||||
// Too close, we are toast
|
||||
if (rx >= - 12 && rx <= 12)
|
||||
{
|
||||
// Enemy explodes as well
|
||||
enemies[e].state = 64;
|
||||
return true;
|
||||
}
|
||||
|
@ -747,18 +908,28 @@ bool player_check(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Check collision shot with enemies
|
||||
bool shots_check(void)
|
||||
{
|
||||
// First shot in list (actually the head, or the shot
|
||||
// before the current one)
|
||||
Shot * ps = shots;
|
||||
bool hit = false;
|
||||
|
||||
// Loop over all five enemies
|
||||
for(char i=0; i<5; i++)
|
||||
{
|
||||
// Only check live and active enemies
|
||||
if (enemies[i].state & 0x80)
|
||||
{
|
||||
// Enemy position on screen
|
||||
sbyte rx = (enemies[i].px - player.spx) >> 3;
|
||||
|
||||
// Check for actually on screen
|
||||
if (rx >= 0 && rx < 40)
|
||||
{
|
||||
// Ignore all shots in tile rows before the
|
||||
// current row
|
||||
Shot * ss = ps->next;
|
||||
while (ss->ty < i)
|
||||
{
|
||||
|
@ -766,16 +937,25 @@ bool shots_check(void)
|
|||
ss = ps->next;
|
||||
}
|
||||
|
||||
// Now loop over all shots in the current tile row
|
||||
while (ss->ty == i)
|
||||
{
|
||||
// Check horizontal collision
|
||||
if (xcollision[(char)(ss->x - rx)])
|
||||
{
|
||||
// Remove shot from active list and put it into free list
|
||||
ps->next = ss->next;
|
||||
ss->next = freeShot;
|
||||
freeShot = ss;
|
||||
|
||||
// Mark enemy as exploding
|
||||
enemies[i].state = 64;
|
||||
|
||||
// Increment score
|
||||
game.escore++;
|
||||
hit = true;
|
||||
|
||||
// Done checking this row
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -789,6 +969,7 @@ bool shots_check(void)
|
|||
return hit;
|
||||
}
|
||||
|
||||
// Advance game state
|
||||
void game_state(GameState state)
|
||||
{
|
||||
// Set new state
|
||||
|
@ -823,8 +1004,10 @@ void game_state(GameState state)
|
|||
}
|
||||
}
|
||||
|
||||
// Work for current frame
|
||||
void game_loop()
|
||||
{
|
||||
// Draw the background
|
||||
tiles_draw(player.spx & 4095);
|
||||
|
||||
switch (game.state)
|
||||
|
@ -832,15 +1015,20 @@ void game_loop()
|
|||
case GS_START:
|
||||
break;
|
||||
case GS_READY:
|
||||
// Scroll to start position
|
||||
player.spx -= game.count >> 2;
|
||||
|
||||
if (!--game.count)
|
||||
game_state(GS_PLAYING);
|
||||
break;
|
||||
case GS_PLAYING:
|
||||
// Control player
|
||||
player_control();
|
||||
|
||||
// Move player
|
||||
player_move();
|
||||
|
||||
// Check for new enemies
|
||||
if (game.edelay)
|
||||
{
|
||||
game.edelay--;
|
||||
|
@ -849,30 +1037,38 @@ void game_loop()
|
|||
}
|
||||
else
|
||||
{
|
||||
// Move enemies
|
||||
enemies_move();
|
||||
|
||||
// All enemies gone, update delay for next squad
|
||||
if (!game.ecount)
|
||||
game.edelay = 64 + (rand() & 63);
|
||||
}
|
||||
|
||||
// Check shots
|
||||
if (shots_check())
|
||||
sidfx_play(1, SIDFXExplosion, 1);
|
||||
|
||||
// Check player enemy collision
|
||||
if (player_check())
|
||||
game_state(GS_EXPLODING);
|
||||
break;
|
||||
|
||||
case GS_EXPLODING:
|
||||
// Animate player
|
||||
spr_image(0, 127 - (game.count >> 2));
|
||||
spr_image(7, 127 - (game.count >> 2));
|
||||
|
||||
player_move();
|
||||
enemies_move();
|
||||
|
||||
// Wait for explostion to finish
|
||||
if (!--game.count)
|
||||
{
|
||||
enemies_reset();
|
||||
game.lives--;
|
||||
|
||||
// More lives?
|
||||
if (game.lives)
|
||||
game_state(GS_READY);
|
||||
else
|
||||
|
@ -886,9 +1082,11 @@ void game_loop()
|
|||
}
|
||||
|
||||
// vic.color_border--;
|
||||
// Update sound effects
|
||||
sidfx_loop();
|
||||
// vic.color_border++;
|
||||
|
||||
// Update status line
|
||||
status_update();
|
||||
}
|
||||
|
||||
|
@ -956,10 +1154,13 @@ int main(void)
|
|||
memset(Color, 0, 80);
|
||||
memset(Color + 80, 8, 920);
|
||||
|
||||
// Init sound effects state machine
|
||||
sidfx_init();
|
||||
|
||||
// Full volume
|
||||
sid.fmodevol = 15;
|
||||
|
||||
// Init status line
|
||||
status_init();
|
||||
|
||||
// initialize background parallax stars
|
||||
|
|
Loading…
Reference in New Issue