Fix LF line ending on windows, float and loop optimizations

This commit is contained in:
drmortalwombat 2022-01-25 14:31:36 +01:00
parent 9f41a55a18
commit f25bf80a0f
16 changed files with 712 additions and 45 deletions

View File

@ -1,6 +1,6 @@
#include "joystick.h"
signed char joyx[2], joyy[2];
sbyte joyx[2], joyy[2];
bool joyb[2];
void joy_poll(char n)

View File

@ -1,7 +1,9 @@
#ifndef C64_JOYSTICK_H
#define C64_JOYSTICK_H
extern signed char joyx[2], joyy[2];
#include "types.h"
extern sbyte joyx[2], joyy[2];
extern bool joyb[2];
// poll joystick input for joystick 0 or 1 and place

View File

@ -245,6 +245,14 @@ void rirq_write(RIRQCode * ic, byte n, void * addr, byte data)
ic->code[p] = data;
}
void rirq_call(RIRQCode * ic, byte n, void * addr)
{
byte p = irqai[n];
ic->code[p - 1] = 0x20;
ic->code[p + 0] = (unsigned)addr & 0xff;
ic->code[p + 1] = (unsigned)addr >> 8;
}
void rirq_delay(RIRQCode * ic, byte cycles)
{
ic->code[ 1] = cycles;

View File

@ -40,6 +40,9 @@ void rirq_build(RIRQCode * ic, byte size);
// Add a write command to a raster IRQ
inline void rirq_write(RIRQCode * ic, byte n, void * addr, byte data);
// Add a call command to a raster IRQ
inline void rirq_call(RIRQCode * ic, byte n, void * addr);
// Change the address of a raster IRQ write command
inline void rirq_addr(RIRQCode * ic, byte n, void * addr);

View File

@ -2597,6 +2597,24 @@ bool InterInstruction::ConstantFolding(void)
mNumOperands = 1;
return true;
}
else if (mOperator == IA_MODU && (mSrc[0].mIntConst & (mSrc[0].mIntConst - 1)) == 0)
{
mOperator = IA_AND;
mSrc[0].mIntConst--;
return true;
}
else if (mOperator == IA_DIVU && (mSrc[0].mIntConst & (mSrc[0].mIntConst - 1)) == 0)
{
int n = 0;
while (mSrc[0].mIntConst > 1)
{
n++;
mSrc[0].mIntConst >>= 1;
}
mOperator = IA_SHR;
mSrc[0].mIntConst = n;
return true;
}
}
}
else if (mSrc[1].mTemp < 0)
@ -3005,6 +3023,7 @@ void InterCodeBasicBlock::GenerateTraces(bool expand)
else if (mTrueJump && !mFalseJump && ((expand && mTrueJump->mInstructions.Size() < 10 && mTrueJump->mInstructions.Size() > 1) || mTrueJump->mNumEntries == 1) && !mTrueJump->mLoopHead && !IsInfiniteLoop(mTrueJump, mTrueJump))
{
mTrueJump->mNumEntries--;
int n = mTrueJump->mNumEntries;
mInstructions.Pop();
for (i = 0; i < mTrueJump->mInstructions.Size(); i++)
@ -3013,11 +3032,14 @@ void InterCodeBasicBlock::GenerateTraces(bool expand)
mFalseJump = mTrueJump->mFalseJump;
mTrueJump = mTrueJump->mTrueJump;
if (n > 0)
{
if (mTrueJump)
mTrueJump->mNumEntries++;
if (mFalseJump)
mFalseJump->mNumEntries++;
}
}
else
break;
}
@ -3474,6 +3496,11 @@ void InterCodeBasicBlock::CheckValueUsage(InterInstruction * ins, const GrowingI
ins->mSrc[0].mIntConst = 3;
}
}
else if (ins->mOperator == IA_MODU && (ins->mSrc[0].mIntConst & (ins->mSrc[0].mIntConst - 1)) == 0)
{
ins->mOperator = IA_AND;
ins->mSrc[0].mIntConst--;
}
}
if (ins->mSrc[0].mTemp > 0 && ins->mSrc[1].mTemp > 0 && ins->mSrc[0].mTemp == ins->mSrc[1].mTemp)
@ -3915,6 +3942,17 @@ void InterCodeBasicBlock::SimplifyIntegerRangeRelops(void)
{
mInstructions[i]->mOperator = IA_EXT8TO16U;
}
else if (mInstructions[i]->mCode == IC_BINARY_OPERATOR && mInstructions[i]->mOperator == IA_DIVS && mInstructions[i]->mSrc[0].IsUnsigned() && mInstructions[i]->mSrc[1].IsUnsigned())
{
mInstructions[i]->mOperator = IA_DIVU;
mInstructions[i]->ConstantFolding();
}
else if (mInstructions[i]->mCode == IC_BINARY_OPERATOR && mInstructions[i]->mOperator == IA_MODS && mInstructions[i]->mSrc[0].IsUnsigned() && mInstructions[i]->mSrc[1].IsUnsigned())
{
mInstructions[i]->mOperator = IA_MODU;
mInstructions[i]->ConstantFolding();
}
#endif
}
#endif
@ -6655,16 +6693,16 @@ void InterCodeBasicBlock::FollowJumps(void)
}
InterCodeBasicBlock* InterCodeBasicBlock::PropagateDominator(InterCodeProcedure* proc)
InterCodeBasicBlock* InterCodeBasicBlock::BuildLoopPrefix(InterCodeProcedure* proc)
{
if (!mVisited)
{
mVisited = true;
if (mTrueJump)
mTrueJump = mTrueJump->PropagateDominator(proc);
mTrueJump = mTrueJump->BuildLoopPrefix(proc);
if (mFalseJump)
mFalseJump = mFalseJump->PropagateDominator(proc);
mFalseJump = mFalseJump->BuildLoopPrefix(proc);
if (mLoopHead)
{
@ -8458,7 +8496,7 @@ void InterCodeProcedure::Close(void)
TempForwarding();
}
BuildDominators();
BuildLoopPrefix();
DisassembleDebug("added dominators");
ResetVisited();
@ -8655,6 +8693,21 @@ void InterCodeProcedure::Close(void)
#endif
#if 1
BuildLoopPrefix();
DisassembleDebug("added dominators");
ResetVisited();
mEntryBlock->SingleBlockLoopOptimisation(mParamAliasedSet);
DisassembleDebug("single block loop opt 2");
BuildDataFlowSets();
BuildTraces(false);
DisassembleDebug("Rebuilt traces");
#endif
#if 1
ResetVisited();
mEntryBlock->PeepholeOptimization();
@ -8848,10 +8901,14 @@ void InterCodeProcedure::MergeBasicBlocks(void)
}
void InterCodeProcedure::BuildDominators(void)
void InterCodeProcedure::BuildLoopPrefix(void)
{
ResetVisited();
mEntryBlock = mEntryBlock->PropagateDominator(this);
for (int i = 0; i < mBlocks.Size(); i++)
mBlocks[i]->mLoopPrefix = nullptr;
mEntryBlock = mEntryBlock->BuildLoopPrefix(this);
ResetVisited();
for (int i = 0; i < mBlocks.Size(); i++)
@ -9002,7 +9059,7 @@ void InterCodeProcedure::Disassemble(FILE* file)
static char typechars[] = "NBCILFP";
for (int i = 0; i < mTemporaries.Size(); i++)
{
fprintf(file, "$%02x T%d(%c), ", mTempOffset[i], i, typechars[mTemporaries[i]]);
fprintf(file, "$%02x R%d(%c), ", mTempOffset[i], i, typechars[mTemporaries[i]]);
}
fprintf(file, "\n");

View File

@ -445,7 +445,7 @@ public:
void CollectLoopPath(const GrowingArray<InterCodeBasicBlock*>& body, GrowingArray<InterCodeBasicBlock*>& path);
void InnerLoopOptimization(const NumberSet& aliasedParams);
InterCodeBasicBlock* PropagateDominator(InterCodeProcedure * proc);
InterCodeBasicBlock* BuildLoopPrefix(InterCodeProcedure * proc);
void SplitBranches(InterCodeProcedure* proc);
void FollowJumps(void);
@ -517,7 +517,7 @@ protected:
void TempForwarding(void);
void RemoveUnusedInstructions(void);
bool GlobalConstantPropagation(void);
void BuildDominators(void);
void BuildLoopPrefix(void);
void SingleAssignmentForwarding(void);
void MergeBasicBlocks(void);

View File

@ -5106,6 +5106,7 @@ NativeCodeBasicBlock* NativeCodeBasicBlock::BinaryOperator(InterCodeProcedure* p
{
int sop0 = 0, sop1 = 1;
bool flipop = false;
bool changedSign = false;
if (ins->mOperator == IA_ADD || ins->mOperator == IA_MUL || ins->mOperator == IA_SUB)
{
@ -5128,6 +5129,13 @@ NativeCodeBasicBlock* NativeCodeBasicBlock::BinaryOperator(InterCodeProcedure* p
if (ins->mSrc[sop1].mTemp < 0)
{
union { float f; unsigned int v; } cc;
if (ins->mOperator == IA_SUB && flipop)
{
changedSign = true;
cc.f = -ins->mSrc[sop1].mFloatConst;
}
else
cc.f = ins->mSrc[sop1].mFloatConst;
mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff));
@ -5168,6 +5176,13 @@ NativeCodeBasicBlock* NativeCodeBasicBlock::BinaryOperator(InterCodeProcedure* p
if (ins->mSrc[sop0].mTemp < 0)
{
union { float f; unsigned int v; } cc;
if (ins->mOperator == IA_SUB && !flipop)
{
changedSign = true;
cc.f = -ins->mSrc[sop0].mFloatConst;
}
else
cc.f = ins->mSrc[sop0].mFloatConst;
mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_IMMEDIATE, cc.v & 0xff));
@ -5213,6 +5228,8 @@ NativeCodeBasicBlock* NativeCodeBasicBlock::BinaryOperator(InterCodeProcedure* p
mIns.Push(NativeCodeInstruction(ASMIT_JSR, ASMIM_ABSOLUTE, art.mOffset, art.mLinkerObject, NCIF_RUNTIME));
} break;
case IA_SUB:
{
if (!changedSign)
{
if (flipop)
{
@ -5226,6 +5243,7 @@ NativeCodeBasicBlock* NativeCodeBasicBlock::BinaryOperator(InterCodeProcedure* p
mIns.Push(NativeCodeInstruction(ASMIT_EOR, ASMIM_IMMEDIATE, 0x80));
mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_WORK + 3));
}
}
NativeCodeGenerator::Runtime& art(nproc->mGenerator->ResolveRuntime(Ident::Unique("faddsub")));
mIns.Push(NativeCodeInstruction(ASMIT_JSR, ASMIM_ABSOLUTE, art.mOffset, art.mLinkerObject, NCIF_RUNTIME));
@ -15103,18 +15121,34 @@ void NativeCodeProcedure::Compile(InterCodeProcedure* proc)
mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK));
mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_SBC, ASMIM_IMMEDIATE, commonFrameSize & 0xff));
mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK));
if (commonFrameSize >= 256)
{
mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1));
mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_SBC, ASMIM_IMMEDIATE, (commonFrameSize >> 8) & 0xff));
mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1));
}
else
{
mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_BCS, ASMIM_RELATIVE, 2));
mEntryBlock->mIns.Push(NativeCodeInstruction(ASMIT_DEC, ASMIM_ZERO_PAGE, BC_REG_STACK + 1));
}
mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_CLC, ASMIM_IMPLIED));
mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK));
mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_ADC, ASMIM_IMMEDIATE, commonFrameSize & 0xff));
mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK));
if (commonFrameSize >= 256)
{
mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_LDA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1));
mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_ADC, ASMIM_IMMEDIATE, (commonFrameSize >> 8) & 0xff));
mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_STA, ASMIM_ZERO_PAGE, BC_REG_STACK + 1));
}
else
{
mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_BCC, ASMIM_RELATIVE, 2));
mExitBlock->mIns.Push(NativeCodeInstruction(ASMIT_INC, ASMIM_ZERO_PAGE, BC_REG_STACK + 1));
}
}
if (mNoFrame)
{

View File

@ -88,7 +88,7 @@ bool SourceFile::Open(const char* name, const char* path, bool binary)
strcat_s(fname + n, sizeof(fname) - n, name);
if (!fopen_s(&mFile, fname, binary ? "rb" : "r"))
if (!fopen_s(&mFile, fname, "rb"))
{
_fullpath(mFileName, fname, sizeof(mFileName));
char* p = mFileName;
@ -158,7 +158,7 @@ bool Preprocessor::NextLine(void)
mLocation.mLine++;
s = strlen(mLine);
while (s > 0 && mLine[s - 1] == '\n')
while (s > 0 && (mLine[s - 1] == '\n' || mLine[s - 1] == '\r'))
s--;
if (s == 0 || mLine[s - 1] != '\\')
return true;

View File

@ -73,7 +73,7 @@ int main(int argc, const char** argv)
#else
strcpy(strProductName, "oscar64");
strcpy(strProductVersion, "1.2.66");
strcpy(strProductVersion, "1.2.67");
#ifdef __APPLE__
uint32_t length = sizeof(basePath);

View File

@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,2,66,0
PRODUCTVERSION 1,2,66,0
FILEVERSION 1,2,67,0
PRODUCTVERSION 1,2,67,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -43,12 +43,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "oscar64"
VALUE "FileDescription", "oscar64 compiler"
VALUE "FileVersion", "1.2.66.0"
VALUE "FileVersion", "1.2.67.0"
VALUE "InternalName", "oscar64.exe"
VALUE "LegalCopyright", "Copyright (C) 2021"
VALUE "OriginalFilename", "oscar64.exe"
VALUE "ProductName", "oscar64"
VALUE "ProductVersion", "1.2.66.0"
VALUE "ProductVersion", "1.2.67.0"
END
END
BLOCK "VarFileInfo"

View File

@ -3169,15 +3169,15 @@
{
"Name" = "8:Microsoft Visual Studio"
"ProductName" = "8:oscar64"
"ProductCode" = "8:{71F1F5C2-A7B5-46C1-8354-C7B870ADCD34}"
"PackageCode" = "8:{7F48C21E-E13E-479D-824A-5AB731A09EB1}"
"ProductCode" = "8:{118A6EF7-D160-4A3E-AECC-5B0D9AA16099}"
"PackageCode" = "8:{8B7C9187-A38D-43DB-B458-3E80FDB17616}"
"UpgradeCode" = "8:{9AB61EFF-ACAC-4079-9950-8D96615CD4EF}"
"AspNetVersion" = "8:2.0.50727.0"
"RestartWWWService" = "11:FALSE"
"RemovePreviousVersions" = "11:TRUE"
"DetectNewerInstalledVersion" = "11:TRUE"
"InstallAllUsers" = "11:FALSE"
"ProductVersion" = "8:1.2.66"
"ProductVersion" = "8:1.2.67"
"Manufacturer" = "8:oscar64"
"ARPHELPTELEPHONE" = "8:"
"ARPHELPLINK" = "8:"

279
samples/games/lander.c Normal file
View File

@ -0,0 +1,279 @@
#include <c64/joystick.h>
#include <c64/vic.h>
#include <c64/sprites.h>
#include <stdlib.h>
#include <string.h>
byte landersprites[] = {
#embed "../resources/landersprites.bin"
};
// Screen and color ram address
#define Screen ((byte *)0x0400)
#define Color ((byte *)0xd800)
struct Lander
{
float px, py, vx, vy;
};
enum GameState
{
GS_READY, // Getting ready
GS_PLAYING, // Playing the game
GS_LANDED, // Landed on pad
GS_COLLIDE // Collided with something
};
// State of the game
struct Game
{
GameState state;
byte count;
Lander lander; // use an array for multiplayer
} TheGame; // Only one game, so global variable
// Put one char on screen
inline void screen_put(byte x, byte y, char ch, char color)
{
__assume(y < 25);
Screen[40 * y + x] = ch;
Color[40 * y + x] = color;
}
// Get one char from screen
inline char screen_get(byte x, byte y)
{
__assume(y < 25);
return Screen[40 * y + x];
}
void screen_init(void)
{
// Fill screen with spaces
memset(Screen, ' ', 1000);
for(char i=0; i<100; i++)
screen_put(rand() % 40, rand() % 25, '.', VCOL_WHITE)
sbyte height[41];
for(char i=0; i<41; i+=8)
height[i] = 4 + rand() % 16;
for(char step = 8; step > 1; step /= 2)
{
for(char i=0; i<40; i+=step)
{
char p = (height[i] + height[i + step]) >> 1;
p += rand() % step - (step / 2);
height[i + step / 2] = p;
}
}
char xp = 2 + rand() % 33;
char yp = height[xp];
for(char i=1; i<4; i++)
if (height[xp + i] < yp)
yp = height[xp + i]
for(char i=0; i<4; i++)
height[xp + i] = yp;
for(char x=0; x<40; x++)
{
char h = height[x];
for(char y=0; y<h; y++)
screen_put(x, 24 - y, 160, VCOL_YELLOW)
}
for(char i=0; i<4; i++)
{
screen_put(xp + i, 24 - yp, 128 + 86, VCOL_MED_GREY)
screen_put(xp + i, 23 - yp, 100, VCOL_WHITE)
}
}
void lander_init(Lander * lander)
{
lander->px = 160;
lander->py = 50;
lander->vx = 0;
lander->vy = 0;
spr_set(0, true, (int)lander->px, (int)lander->py, 0x0380 / 64, VCOL_DARK_GREY, false, false, false);
spr_set(1, true, (int)lander->px, (int)lander->py, 0x0340 / 64, VCOL_LT_GREY, true, false, false);
spr_set(2, false, (int)lander->px, (int)lander->py + 20, 0x03c0 / 64, VCOL_WHITE, false, false, false);
}
char ExhaustColor[] = {VCOL_YELLOW, VCOL_WHITE, VCOL_ORANGE, VCOL_LT_BLUE};
void lander_move(Lander * lander, sbyte jx, sbyte jy)
{
lander->px += lander->vx;
lander->py += lander->vy;
lander->vx += jx * 0.02;
lander->vy += jy * 0.1 + 0.01;
}
void lander_show(Lander * lander, sbyte jx, sbyte jy)
{
vic.color_border++;
int ix = (int)lander->px, iy = (int)lander->py;
spr_move(0, ix, iy);
spr_move(1, ix, iy);
if (jy < 0)
{
spr_move(2, ix, iy + 20);
spr_color(2, ExhaustColor[rand() & 3]);
spr_show(2, true);
}
else
spr_show(2, false);
vic.color_border--;
}
void lander_flash(Lander * lander, char c)
{
spr_color(0, rand() & 1);
}
enum LanderCollision
{
LCOL_FREE,
LCOL_GROUND,
LCOL_PAD
};
LanderCollision lander_check(Lander * lander)
{
sbyte ix = (sbyte)((lander->px - 24) * 0.125);
sbyte iy = (sbyte)((lander->py - 29) * 0.125);
if (iy > 24)
return LCOL_GROUND;
if (iy < 0)
return LCOL_FREE;
LanderCollision col = LCOL_FREE;
for(char i=0; i<4; i++)
{
if (ix >= 0 && ix < 40)
{
char ch = screen_get(ix, iy);
if (ch == 160)
return LCOL_GROUND;
else if (ch == 128 + 86)
col = LCOL_PAD;
}
ix++;
}
return col;
}
void game_state(GameState state)
{
// Set new state
TheGame.state = state;
switch(state)
{
case GS_READY:
// Clear the screen
lander_init(&TheGame.lander)
screen_init();
TheGame.count = 32;
break;
case GS_PLAYING:
break;
case GS_LANDED:
TheGame.lander.py = (sbyte)((TheGame.lander.py - 29) * 0.125) * 8 + 28;
lander_show(&TheGame.lander, 0, 0);
TheGame.count = 16;
break;
case GS_COLLIDE:
TheGame.count = 32;
lander_show(&TheGame.lander, 0, 0);
break;
}
}
// Main game loop, invoked every vsync
void game_loop(void)
{
switch (TheGame.state)
{
case GS_READY:
// Countdown ready to start
if (!--TheGame.count)
game_state(GS_PLAYING);
break;
case GS_PLAYING:
{
// Check player input on every frame
joy_poll(0);
lander_move(&TheGame.lander, joyx[0], joyy[0]);
LanderCollision col = lander_check(&TheGame.lander);
if (col == LCOL_GROUND)
game_state(GS_COLLIDE);
else if (col == LCOL_PAD)
game_state(GS_LANDED);
else
lander_show(&TheGame.lander, joyx[0], joyy[0]);
} break;
case GS_LANDED:
if (!--TheGame.count)
game_state(GS_READY);
break;
case GS_COLLIDE:
lander_flash(&TheGame.lander, TheGame.count);
if (!--TheGame.count)
game_state(GS_READY);
break;
}
}
int main(void)
{
// Screen color to black
vic.color_border = VCOL_BLACK;
vic.color_back = VCOL_BLACK;
vic.spr_mcolor0 = VCOL_MED_GREY;
vic.spr_mcolor1 = VCOL_YELLOW;
memcpy((char *)0x0340, landersprites, 192);
spr_init(Screen);
// Start the game in ready state
game_state(GS_READY);
// Forever
for(;;)
{
// One game loop iteration
game_loop();
// Wait one vsync
vic_waitFrame();
}
// Never reached
return 0;
}

2
samples/games/make.bat Normal file
View File

@ -0,0 +1,2 @@
call ..\..\bin\oscar64 snake.c
call ..\..\bin\oscar64 -n lander.c

282
samples/games/snake.c Normal file
View File

@ -0,0 +1,282 @@
#include <c64/joystick.h>
#include <c64/vic.h>
#include <stdlib.h>
#include <string.h>
// Position/Direction on screen
struct Point
{
sbyte x, y;
};
struct Snake
{
Point head; // Position of head
Point dir; // Direction of head
Point tail[256]; // Position of tail
byte length; // Length of tail
byte pos; // Tail start
};
enum GameState
{
GS_READY, // Getting ready
GS_PLAYING, // Playing the game
GS_COLLIDE // Collided with something
};
// State of the game
struct Game
{
GameState state;
byte count;
Snake snake; // use an array for multiplayer
} TheGame; // Only one game, so global variable
// Screen and color ram address
#define Screen ((byte *)0x0400)
#define Color ((byte *)0xd800)
// Put one char on screen
inline void screen_put(byte x, byte y, char ch, char color)
{
Screen[40 * y + x] = ch;
Color[40 * y + x] = color;
}
// Get one char from screen
inline char screen_get(byte x, byte y)
{
return Screen[40 * y + x];
}
// Put a fruit/heart at random position
void screen_fruit(void)
{
byte x, y;
do
{
// Draw a random position
x = 1 + rand() % 38;
y = 1 + rand() % 23;
// Ensure it is an empty place
} while (screen_get(x, y) != ' ');
// Put the heart on screen
screen_put(x, y, 83, VCOL_YELLOW);
}
// Clear screen and draw borders
void screen_init(void)
{
// Fill screen with spaces
memset(Screen, ' ', 1000);
// Bottom and top row
for(byte x=0; x<40; x++)
{
screen_put(x, 0, 0xa0, VCOL_LT_GREY);
screen_put(x, 24, 0xa0, VCOL_LT_GREY);
}
// Left and right column
for(byte y=0; y<25; y++)
{
screen_put( 0, y, 0xa0, VCOL_LT_GREY);
screen_put( 39, y, 0xa0, VCOL_LT_GREY);
}
}
// Initialize a snake
void snake_init(Snake * s)
{
// Length of tail is one
s->length = 1;
s->pos = 0;
// Snake in the center of screen
s->head.x = 20;
s->head.y = 12;
// Starting to the right
s->dir.x = 1;
s->dir.y = 0;
// Show head
screen_put(s->head.x, s->head.y, 81, VCOL_WHITE);
}
bool snake_advance(Snake * s)
{
// Promote head to start of tail
s->tail[s->pos] = s->head;
s->pos++;
screen_put(s->head.x, s->head.y, 81, VCOL_LT_BLUE);
// Advance head
s->head.x += s->dir.x;
s->head.y += s->dir.y;
// Get character at new head position
char ch = screen_get(s->head.x, s->head.y);
// Draw head
screen_put(s->head.x, s->head.y, 81, VCOL_WHITE);
// Clear tail
char tpos = s->pos - s->length;
screen_put(s->tail[tpos].x, s->tail[tpos].y, ' ', VCOL_BLACK);
// Did snake collect the fruit
if (ch == 83)
{
// Extend tail
s->length++;
screen_fruit();
}
else if (ch != ' ')
{
// Snake collided with something
return true;
}
return false;
}
// flash the snake after collision
bool snake_flash(Snake * s, char c)
{
// Loop over all tail elements
for(char i=0; i<s->length; i++)
{
// Set color
char tpos = s->pos - i - 1;
screen_put(s->tail[tpos].x, s->tail[tpos].y, 81, c);
}
}
// Change snake direction based on user input
void snake_control(Snake * s, sbyte jx, sbyte jy)
{
// First change from horizontal to vertical, otherwise
// check vertical to horizontal
if (s->dir.x && jy)
{
s->dir.x = 0;
s->dir.y = jy;
}
else if (s->dir.y && jx)
{
s->dir.y = 0;
s->dir.x = jx;
}
}
void game_state(GameState state)
{
// Set new state
TheGame.state = state;
switch(state)
{
case GS_READY:
// Clear the screen
screen_init();
TheGame.count = 32;
break;
case GS_PLAYING:
// Init the snake
snake_init(&TheGame.snake);
// Initial fruit
screen_fruit();
TheGame.count = 16;
break;
case GS_COLLIDE:
TheGame.count = 16;
break;
}
}
// Colors for collision "animation"
char FlashColors[] = {
VCOL_YELLOW,
VCOL_WHITE,
VCOL_LT_GREY,
VCOL_YELLOW,
VCOL_ORANGE,
VCOL_RED,
VCOL_MED_GREY,
VCOL_DARK_GREY
};
// Main game loop, invoked every vsync
void game_loop(void)
{
switch (TheGame.state)
{
case GS_READY:
// Countdown ready to start
if (!--TheGame.count)
game_state(GS_PLAYING);
break;
case GS_PLAYING:
// Check player input on every frame
joy_poll(0);
snake_control(&TheGame.snake, joyx[0], joyy[0]);
if (!--TheGame.count)
{
// Move snake every four frames, advance to collision
// state if collided
if (snake_advance(&TheGame.snake))
game_state(GS_COLLIDE);
else
TheGame.count = 4;
}
break;
case GS_COLLIDE:
// Flash the collided snake
snake_flash(&TheGame.snake, FlashColors[(16 - TheGame.count) / 2]);
if (!--TheGame.count)
game_state(GS_READY);
break;
}
}
int main(void)
{
// Screen color to black
vic.color_border = VCOL_BLACK;
vic.color_back = VCOL_BLACK;
// Start the game in ready state
game_state(GS_READY);
// Forever
for(;;)
{
// One game loop iteration
game_loop();
// Wait one vsync
vic_waitFrame();
}
// Never reached
return 0;
}

Binary file not shown.

Binary file not shown.