diff --git a/README.md b/README.md index b3fa5a5..1bc6e8c 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ The compiler can also be built using MSVC or GCC. A visual studio project and a The compiler is command line driven, and creates an executable .prg file. - oscar64 {-i=includePath} [-o=output.prg] [-rt=runtime.c] [-tf=format] [-e] [-n] [-dSYMBOL[=value]] {source.c} + oscar64 {-i=includePath} [-o=output.prg] [-rt=runtime.c] [-tf=format] [-tm=machine] [-e] [-n] [-dSYMBOL[=value]] {source.c} * -v : verbose output for diagnostics * -v2 : more verbose output diff --git a/include/c64/iecbus.c b/include/c64/iecbus.c new file mode 100644 index 0000000..1a93755 --- /dev/null +++ b/include/c64/iecbus.c @@ -0,0 +1,238 @@ +#include "iecbus.h" +#include +#include + +IEC_STATUS iec_status; + +#define CIA2B_ATNOUT 0x08 +#define CIA2B_CLKOUT 0x10 +#define CIA2B_DATAOUT 0x20 + +#define CIA2B_CLKIN 0x40 +#define CIA2B_DATAIN 0x80 + +#pragma optimize(push) +#pragma optimize(1) + +static void delay(char n) +{ + while (n) + { + __asm { + nop + } + n--; + } +} + +static inline void data_low(void) +{ + cia2.pra &= ~CIA2B_DATAOUT; +} + +static inline void data_high(void) +{ + cia2.pra |= CIA2B_DATAOUT; +} + +static inline void clock_low(void) +{ + cia2.pra &= ~CIA2B_CLKOUT; +} + +static inline void cdata_low(void) +{ + cia2.pra &= ~(CIA2B_CLKOUT | CIA2B_DATAOUT); +} + +static inline void clock_high(void) +{ + cia2.pra |= CIA2B_CLKOUT; +} + +static inline void atn_low(void) +{ + cia2.pra &= ~CIA2B_ATNOUT; +} + +static inline void atn_high(void) +{ + cia2.pra |= CIA2B_ATNOUT; +} + +static bool data_check(void) +{ + char cnt = 100; + while (cnt > 0 && (cia2.pra & CIA2B_DATAIN)) + cnt--; + + if (cnt) + return true; + else + { + iec_status = IEC_TIMEOUT; + return false; + } +} + +bool iec_eoi(void) +{ + cdata_low(); + + while (!(cia2.pra & CIA2B_DATAIN)) + ; + delay(50); + + return data_check(); +} + +bool iec_write(char b) +{ + cdata_low(); + + while (!(cia2.pra & CIA2B_DATAIN)) + ; + + for(char i=0; i<8; i++) + { + clock_high(); + if (b & 1) + data_low(); + else + data_high(); + clock_low(); + b >>= 1; + __asm + { + nop + nop + nop + nop + } + } + data_low(); + clock_high(); + + return data_check(); +} + +char iec_read(void) +{ + while (!(cia2.pra & CIA2B_CLKIN)) + ; + + data_low(); + + char cnt = 100; + while (cnt > 0 && (cia2.pra & CIA2B_CLKIN)) + cnt--; + + if (cnt == 0) + { + iec_status = IEC_EOF; + data_high(); + __asm + { + nop + nop + nop + nop + } + data_low(); + + cnt = 200; + while (cnt > 0 && (cia2.pra & CIA2B_CLKIN)) + cnt--; + + if (cnt == 0) + { + iec_status = IEC_TIMEOUT; + return 0; + } + } + + char b = 0; + for(char i=0; i<8; i++) + { + char c; + while (!((c = cia2.pra) & CIA2B_CLKIN)) + ; + + b >>= 1; + b |= c & 0x80; + + while (cia2.pra & CIA2B_CLKIN) + ; + } + + data_high(); + + return b; +} + +void iec_atn(char dev, char sec) +{ + atn_high(); + clock_high(); + + delay(100); + + iec_write(dev); + if (sec != 0xff) + iec_write(sec); + + data_high(); + atn_low(); +} + + +void iec_talk(char dev, char sec) +{ + iec_status = IEC_OK; + + iec_atn(dev | 0x40, sec); + clock_low(); +} + +void iec_untalk(void) +{ + iec_atn(0x5f, 0xff); +} + +void iec_listen(char dev, char sec) +{ + iec_status = IEC_OK; + + iec_atn(dev | 0x20, sec); +} + +void iec_unlisten(void) +{ + iec_atn(0x3f, 0xff); +} + +void iec_open(char dev, char sec, const char * fname) +{ + iec_status = IEC_OK; + + iec_atn(dev | 0x20, sec | 0xf0); + char i = 0; + while (fname[i]) + { + if (!fname[i + 1]) + iec_eoi(); + iec_write(fname[i]); + i++; + } + iec_unlisten(); +} + +void iec_close(char dev, char sec) +{ + iec_atn(dev | 0x20, sec | 0xe0); + iec_unlisten(); +} + + +#pragma optimize(pop) + diff --git a/include/c64/iecbus.h b/include/c64/iecbus.h new file mode 100644 index 0000000..b3f03b4 --- /dev/null +++ b/include/c64/iecbus.h @@ -0,0 +1,38 @@ +#ifndef C64_IECBUS_H +#define C64_IECBUS_H + +enum IEC_STATUS +{ + IEC_OK = 0x00, + IEC_EOF = 0x01, + + IEC_ERROR = 0x80, + IEC_TIMEOUT +}; + +extern IEC_STATUS iec_status; + +bool iec_eoi(void); + +bool iec_write(char b); + +char iec_read(void); + +void iec_atn(char dev, char sec); + +void iec_talk(char dev, char sec); + +void iec_untalk(void); + +void iec_listen(char dev, char sec); + +void iec_unlisten(void); + +void iec_open(char dev, char sec, const char * fname); + +void iec_close(char dev, char sec); + +#pragma compile("iecbus.c") + +#endif + diff --git a/include/nes/mmc1.h b/include/nes/mmc1.h index b302d17..c7583d6 100644 --- a/include/nes/mmc1.h +++ b/include/nes/mmc1.h @@ -1,7 +1,6 @@ #ifndef NES_MMC1_H #define NES_MMC1_H - #include enum MMC1Mirror diff --git a/oscar64/NativeCodeGenerator.cpp b/oscar64/NativeCodeGenerator.cpp index 43355a3..34de0e3 100644 --- a/oscar64/NativeCodeGenerator.cpp +++ b/oscar64/NativeCodeGenerator.cpp @@ -19226,14 +19226,14 @@ bool NativeCodeBasicBlock::JoinTailCodeSequences(NativeCodeProcedure* proc, bool int ns = mIns.Size(); if ((mIns[ns - 2].mType == ASMIT_INY || mIns[ns - 2].mType == ASMIT_DEY) && mIns[ns - 1].mType == ASMIT_TYA) { - if (mBranch == ASMIT_BEQ && !mFalseJump->mEntryRequiredRegs[CPU_REG_A]) + if (mBranch == ASMIT_BEQ && !mFalseJump->mEntryRequiredRegs[CPU_REG_A] && mTrueJump->mNumEntries == 1) { mTrueJump->mIns.Insert(0, NativeCodeInstruction(mIns[ns - 1].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); mIns[ns - 1].mType = ASMIT_NOP; mIns[ns - 2].mLive |= LIVE_CPU_REG_Z; changed = true; } - else if (mBranch == ASMIT_BNE && !mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + else if (mBranch == ASMIT_BNE && !mTrueJump->mEntryRequiredRegs[CPU_REG_A] && mFalseJump->mNumEntries == 1) { mFalseJump->mIns.Insert(0, NativeCodeInstruction(mIns[ns - 1].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); mIns[ns - 1].mType = ASMIT_NOP; @@ -19243,14 +19243,14 @@ bool NativeCodeBasicBlock::JoinTailCodeSequences(NativeCodeProcedure* proc, bool } else if ((mIns[ns - 2].mType == ASMIT_INX || mIns[ns - 2].mType == ASMIT_DEX) && mIns[ns - 1].mType == ASMIT_TXA) { - if (mBranch == ASMIT_BEQ && !mFalseJump->mEntryRequiredRegs[CPU_REG_A]) + if (mBranch == ASMIT_BEQ && !mFalseJump->mEntryRequiredRegs[CPU_REG_A] && mTrueJump->mNumEntries == 1) { mTrueJump->mIns.Insert(0, NativeCodeInstruction(mIns[ns - 1].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); mIns[ns - 1].mType = ASMIT_NOP; mIns[ns - 2].mLive |= LIVE_CPU_REG_Z; changed = true; } - else if (mBranch == ASMIT_BNE && !mTrueJump->mEntryRequiredRegs[CPU_REG_A]) + else if (mBranch == ASMIT_BNE && !mTrueJump->mEntryRequiredRegs[CPU_REG_A] && mFalseJump->mNumEntries == 1) { mFalseJump->mIns.Insert(0, NativeCodeInstruction(mIns[ns - 1].mIns, ASMIT_LDA, ASMIM_IMMEDIATE, 0)); mIns[ns - 1].mType = ASMIT_NOP; @@ -19283,6 +19283,37 @@ bool NativeCodeBasicBlock::JoinTailCodeSequences(NativeCodeProcedure* proc, bool } } #endif + if (mIns.Size() >= 2 && mNumEntries == 1) + { + if (mIns[0].mType == ASMIT_TYA && mIns[1].IsShift() && mIns[1].mMode == ASMIM_IMPLIED) + { + NativeCodeBasicBlock* block = mEntryBlocks[0]; + int ns = block->mIns.Size(); + if (ns >= 2 && block->mIns[ns - 2].mType == ASMIT_TAY && block->mIns[ns - 1].IsSame(mIns[1])) + { + mIns[0].mType = ASMIT_NOP; mIns[0].mMode = ASMIM_IMPLIED; + mIns[1].mType = ASMIT_NOP; mIns[1].mMode = ASMIM_IMPLIED; + block->mIns[ns - 1].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + changed = true; + } + } + } + if (mIns.Size() >= 1 && mNumEntries == 1) + { + if (mIns[0].IsShift() && mIns[0].mMode == ASMIM_ZERO_PAGE && !(mIns[0].mLive & LIVE_MEM)) + { + NativeCodeBasicBlock* block = mEntryBlocks[0]; + int ns = block->mIns.Size(); + if (ns >= 2 && + block->mIns[ns - 2].mType == ASMIT_STA && block->mIns[ns - 2].SameEffectiveAddress(mIns[0]) && + block->mIns[ns - 1].mType == mIns[0].mType && block->mIns[ns - 1].mMode == ASMIM_IMPLIED && !(mIns[0].mLive & LIVE_MEM)) + { + mIns[0].mType = ASMIT_NOP; mIns[0].mMode = ASMIM_IMPLIED; + block->mIns[ns - 1].mLive |= LIVE_CPU_REG_A | LIVE_CPU_REG_C; + changed = true; + } + } + } #if 1 if (mIns.Size() >= 2) { @@ -20300,6 +20331,47 @@ bool NativeCodeBasicBlock::FoldLoopEntry(void) } } } + if (!changed && mTrueJump && mFalseJump && mIns.Size() >= 2) + { + int sz = mIns.Size(); + + if (mIns[sz - 2].mType == ASMIT_LDA && ((mIns[sz - 1].mType == ASMIT_AND && mIns[sz - 1].mMode == ASMIM_IMMEDIATE) || (mIns[sz - 1].IsShift() && mIns[sz - 1].mMode == ASMIM_IMPLIED))) + { + if (mTrueJump->mIns.Size() == 2 && mTrueJump != this) + { + if (mTrueJump->mIns[0].IsSame(mIns[sz - 2]) && mTrueJump->mIns[1].IsSame(mIns[sz - 1])) + { + if (mBranch == mTrueJump->mBranch && mFalseJump == mTrueJump->mFalseJump && mTrueJump == mTrueJump->mTrueJump || + mBranch == InvertBranchCondition(mTrueJump->mBranch) && mFalseJump == mTrueJump->mTrueJump && mTrueJump == mTrueJump->mFalseJump) + { + mIns[sz - 1].mType = ASMIT_NOP; mIns[sz - 1].mMode = ASMIM_IMPLIED; + mIns[sz - 2].mType = ASMIT_NOP; mIns[sz - 2].mMode = ASMIM_IMPLIED; + mBranch = ASMIT_JMP; + mFalseJump->RemEntryBlock(this); + mFalseJump = nullptr; + changed = true; + } + } + } + if (!changed && mFalseJump->mIns.Size() == 2 && mFalseJump != this) + { + if (mFalseJump->mIns[0].IsSame(mIns[sz - 2]) && mFalseJump->mIns[1].IsSame(mIns[sz - 1])) + { + if (mBranch == mFalseJump->mBranch && mFalseJump == mFalseJump->mFalseJump && mFalseJump == mTrueJump->mTrueJump || + mBranch == InvertBranchCondition(mFalseJump->mBranch) && mFalseJump == mFalseJump->mTrueJump && mTrueJump == mFalseJump->mFalseJump) + { + mIns[sz - 1].mType = ASMIT_NOP; + mIns[sz - 1].mMode = ASMIM_IMPLIED; + mBranch = ASMIT_JMP; + mTrueJump->RemEntryBlock(this); + mTrueJump = mFalseJump; + mFalseJump = nullptr; + changed = true; + } + } + } + } + } if (mTrueJump && mTrueJump->FoldLoopEntry()) changed = true; @@ -33930,6 +34002,17 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; progress = true; } +#if 1 + else if ( + mIns[i + 0].mType == ASMIT_STX && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 1].IsShift() && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 0].mAddress && !(mIns[i + 1].mLive & (LIVE_CPU_REG_A | LIVE_MEM))) + { + mIns[i + 0].mType = ASMIT_TXA; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_A; + mIns[i + 1].mMode = ASMIM_IMPLIED; + progress = true; + } +#endif + #if 0 else if ( mIns[i + 0].mType == ASMIT_LDA && mIns[i + 0].mMode == ASMIM_IMMEDIATE && @@ -35476,6 +35559,16 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass progress = true; } + else if ( + mIns[i + 0].mType == ASMIT_LDA && + mIns[i + 1].IsShift() && mIns[i + 1].mMode == ASMIM_IMPLIED && + mIns[i + 2].mType == mIns[i + 1].mType && mIns[i + 2].SameEffectiveAddress(mIns[i + 0]) && + mIns[i + 3].mType == ASMIT_STA && mIns[i + 3].SameEffectiveAddress(mIns[i + 0])) + { + mIns[i + 2].mType = ASMIT_NOP; mIns[i + 2].mMode = ASMIM_IMPLIED; + mIns[i + 1].mLive |= LIVE_CPU_REG_C; + progress = true; + } else if ( mIns[i + 0].mType == ASMIT_STA && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mType == ASMIT_LDX && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress == mIns[i + 0].mAddress && @@ -36743,7 +36836,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass } else if ( mIns[i + 0].mType == ASMIT_LDA && (mIns[i + 0].mMode == ASMIM_ZERO_PAGE || mIns[i + 0].mMode == ASMIM_ABSOLUTE) && - mIns[i + 1].mType == ASMIT_TAX && + mIns[i + 1].mType == ASMIT_TAX && mIns[i + 2].mType == ASMIT_CLC && mIns[i + 3].mType == ASMIT_ADC && mIns[i + 3].mMode == ASMIM_IMMEDIATE && mIns[i + 3].mAddress == 1 && mIns[i + 4].mType == ASMIT_STA && mIns[i + 0].SameEffectiveAddress(mIns[i + 4]) && !(mIns[i + 4].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z | LIVE_CPU_REG_C))) @@ -36798,7 +36891,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass mIns.Insert(i, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_STA, ASMIM_ZERO_PAGE, a1)); mIns.Insert(i + 1, NativeCodeInstruction(mIns[i + 0].mIns, ASMIT_LDA, ASMIM_ZERO_PAGE, a0)); - + mIns[i + 2].mMode = ASMIM_IMPLIED; mIns[i + 3].mMode = ASMIM_IMPLIED; mIns[i + 4].mMode = ASMIM_IMPLIED; @@ -36807,6 +36900,22 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass progress = true; } #endif +#if 1 + else if ( + mIns[i + 0].mType == ASMIT_AND && mIns[i + 0].mMode == ASMIM_IMMEDIATE && mIns[i + 0].mAddress == 0x80 && + mIns[i + 1].mType == ASMIT_STA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && + mIns[i + 2].mType == ASMIT_LDA && mIns[i + 2].mMode == ASMIM_ZERO_PAGE && mIns[i + 2].mAddress != mIns[i + 1].mAddress && + mIns[i + 3].mType == ASMIT_LSR && mIns[i + 3].mMode == ASMIM_IMPLIED && + mIns[i + 4].mType == ASMIT_ORA && mIns[i + 4].mMode == ASMIM_ZERO_PAGE && mIns[i + 4].mAddress == mIns[i + 1].mAddress && !(mIns[i + 4].mLive & (LIVE_CPU_REG_C | LIVE_MEM))) + { + mIns[i + 0].mType = ASMIT_ASL; mIns[i + 0].mMode = ASMIM_IMPLIED; mIns[i + 0].mLive |= LIVE_CPU_REG_C; + mIns[i + 1].mType = ASMIT_NOP; mIns[i + 1].mMode = ASMIM_IMPLIED; + mIns[i + 2].mLive |= LIVE_CPU_REG_C; + mIns[i + 3].mType = ASMIT_ROR; + mIns[i + 4].mType = ASMIT_NOP; mIns[i + 4].mMode = ASMIM_IMPLIED; + progress = true; + } +#endif #if 0 else if ( mIns[i + 0].mType == ASMIT_LDY && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && @@ -38139,6 +38248,19 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass changed = true; } + else if (pass >= 7 && sz >= 1 && + mIns[sz - 1].mType == ASMIT_AND && mIns[sz - 1].mMode == ASMIM_IMMEDIATE && mIns[sz - 1].mAddress == 0x40 && !(mIns[sz - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C)) && + (mBranch == ASMIT_BEQ || mBranch == ASMIT_BNE) && !mExitRequiredRegs[CPU_REG_Z]) + { + mIns[sz - 1].mType = ASMIT_ASL; mIns[sz - 1].mMode = ASMIM_IMPLIED; + + if (mBranch == ASMIT_BEQ) + mBranch = ASMIT_BPL; + else + mBranch = ASMIT_BMI; + + changed = true; + } if (sz == 1 && mBranch == ASMIT_BNE && mTrueJump == this) { @@ -38946,7 +39068,7 @@ void NativeCodeProcedure::Compile(InterCodeProcedure* proc) { mInterProc = proc; - CheckFunc = !strcmp(mInterProc->mIdent->mString, "fill_screen"); + CheckFunc = !strcmp(mInterProc->mIdent->mString, "iec_read"); int nblocks = proc->mBlocks.Size(); tblocks = new NativeCodeBasicBlock * [nblocks]; @@ -39596,7 +39718,6 @@ void NativeCodeProcedure::Optimize(void) mEntryBlock->ReplaceFinalZeroPageUse(this); } #endif - int t = 0; #if 1 do @@ -39785,8 +39906,6 @@ void NativeCodeProcedure::Optimize(void) } #endif - - #if _DEBUG ResetVisited(); mEntryBlock->CheckBlocks(true); @@ -40174,8 +40293,10 @@ void NativeCodeProcedure::Optimize(void) else cnt++; + } while (changed); + #if 1 ResetVisited(); mEntryBlock->ReduceLocalYPressure(); diff --git a/oscar64/Parser.cpp b/oscar64/Parser.cpp index 1e882a0..9212c2b 100644 --- a/oscar64/Parser.cpp +++ b/oscar64/Parser.cpp @@ -3988,7 +3988,6 @@ void Parser::ParsePragma(void) } else if (ConsumeIdentIf("optimize")) { - mScanner->NextToken(); ConsumeToken(TK_OPEN_PARENTHESIS); if (!ConsumeTokenIf(TK_CLOSE_PARENTHESIS)) { @@ -4026,6 +4025,7 @@ void Parser::ParsePragma(void) default: mErrors->Error(mScanner->mLocation, ERRR_INVALID_NUMBER, "Invalid number"); } + mScanner->NextToken(); } else if (ConsumeIdentIf("asm")) mCompilerOptions |= COPT_OPTIMIZE_ASSEMBLER;