diff --git a/README.md b/README.md index 0f134d7..0551ded 100644 --- a/README.md +++ b/README.md @@ -636,7 +636,7 @@ Expands a 2D 4x4 tile grid at any scroll speed. Uses a raster IRQ to limit the Sprites are independed image blocks, such as players, missiles or enemies that can be shown on top of the background. -#### Control a sprite with a joy stick "joycontrol.c" +#### Control a sprite with a joystick "joycontrol.c" Combines reading the joystick with the library and sprite movement with the library. @@ -644,7 +644,21 @@ Combines reading the joystick with the library and sprite movem ![Large text](samples/sprites/multiplexer.png) -Shows 16 virtual sprites multiplexed from the phyiscal eight sprites with raster interrupts, using the oscar sprite multiplexer library. +Shows 16 virtual sprites multiplexed from the physical eight sprites with raster interrupts, using the oscar sprite multiplexer library. + +#### Use raster IRQ to show 32 sprites "sprmux32.c" + +![Large text](samples/sprites/sprmux32.png) + +Shows 32 virtual sprites multiplexed from the physical eight sprites with raster interrupts, using the oscar sprite multiplexer library. This sample requires command line defines to increase the default number of multiplexed sprites and raster interrupt slots: + + oscar64 -n sprmux32.c -O2 -dVSPRITES_MAX=32 -dNUM_IRQS=28 + +#### Use raster IRQ to show 64 sprites "sprmux64.c" + +![Large text](samples/sprites/sprmux64.png) + +Shows 64 moving sprites using raster interrupts to update the position of the eight physical sprites every 25 screen lines. #### Fill the screen with sprites "creditroll.c" diff --git a/include/c64/sprites.c b/include/c64/sprites.c index e469d15..4f36c99 100644 --- a/include/c64/sprites.c +++ b/include/c64/sprites.c @@ -84,6 +84,16 @@ void spr_move16(char sp, int xpos, int ypos) vic.spr_msbx &= ~(1 << sp); } +int spr_posx(char sp) +{ + return vic.spr_pos[sp].x | ((vic.spr_msbx & (1 << sp)) ? 256 : 0); +} + +int spr_posy(char sp) +{ + return vic.spr_pos[sp].y; +} + void spr_image(char sp, char image) { __assume (sp < 8); diff --git a/include/c64/sprites.h b/include/c64/sprites.h index b99cfdc..69f7cee 100644 --- a/include/c64/sprites.h +++ b/include/c64/sprites.h @@ -19,7 +19,16 @@ inline void spr_show(char sp, bool show); inline void spr_move(char sp, int xpos, int ypos); -// move a sprite to the given position +// get current x position of sprite + +inline int spr_posx(char sp); + +// get current y position of sprite + +inline int spr_posy(char sp); + +// move a sprite to the given position, only uses 16 bit y and 16 bit x, +// moves the sprite to a zero y position if offscreen void spr_move16(char sp, int xpos, int ypos); // change the image of a sprite diff --git a/include/c64/vic.c b/include/c64/vic.c index 21a8df3..26fc964 100644 --- a/include/c64/vic.c +++ b/include/c64/vic.c @@ -16,6 +16,11 @@ void vic_sprxy(byte s, int x, int y) vic.spr_msbx &= ~(1 << s); } +int vic_sprgetx(byte s) +{ + return vic.spr_pos[s].x | ((vic.spr_msbx & (1 << s)) ? 256 : 0); +} + void vic_setmode(VicMode mode, char * text, char * font) { switch (mode) diff --git a/include/c64/vic.h b/include/c64/vic.h index 5a8197c..718ae88 100644 --- a/include/c64/vic.h +++ b/include/c64/vic.h @@ -97,6 +97,9 @@ void vic_setmode(VicMode mode, char * text, char * font); // x MSB inline void vic_sprxy(byte s, int x, int y); +// Read the sprite x position from the LSB and MSB register +inline int vic_sprgetx(byte s); + // wait for the beam to reach the bottom of the visual area inline void vic_waitBottom(void); diff --git a/oscar64/NativeCodeGenerator.cpp b/oscar64/NativeCodeGenerator.cpp index bfa534a..e10cde6 100644 --- a/oscar64/NativeCodeGenerator.cpp +++ b/oscar64/NativeCodeGenerator.cpp @@ -11792,6 +11792,124 @@ bool NativeCodeBasicBlock::OptimizeXYPairUsage(void) return changed; } +bool NativeCodeBasicBlock::MoveAccuTrainUp(int at, int end) +{ + CheckLive(); + + int i = at; + while (i > 0) + { + i--; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == mIns[at].mAddress) + { + if (mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z)) + return false; + + int live = 0; + if (i > 0) + live |= mIns[i - 1].mLive; + + for (int j = i; j < at; j++) + mIns[j].mLive |= mIns[end - 1].mLive; + + for (int j = at; j < end; j++) + { + NativeCodeInstruction ins(mIns[j]); + ins.mLive |= live; + + mIns.Remove(j); + i++; + mIns.Insert(i, ins); + } + + CheckLive(); + + return true; + } + + if (mIns[i].mType == ASMIT_JSR) + return false; + + for (int j = at; j < end; j++) + { + if (mIns[j].RequiresXReg() && mIns[i].ChangesXReg() || mIns[j].ChangesXReg() && mIns[i].RequiresXReg()) + return false; + if (mIns[j].RequiresYReg() && mIns[i].ChangesYReg() || mIns[j].ChangesYReg() && mIns[i].RequiresYReg()) + return false; + if (mIns[j].MayBeChangedOnAddress(mIns[i]) || mIns[i].MayBeChangedOnAddress(mIns[j])) + return false; + } + } + + return false; +} + +bool NativeCodeBasicBlock::MoveAccuTrainsUp(void) +{ + bool changed = false; + + if (!mVisited) + { + mVisited = true; + + FastNumberSet wzero(256); + + int i = 0; + while (i < mIns.Size()) + { + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE) + { + if (mIns[i].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z)) + wzero -= mIns[i].mAddress; + else + wzero += mIns[i].mAddress; + i++; + } + else if (mIns[i].mType == ASMIT_LDA && mIns[i].mMode == ASMIM_ZERO_PAGE && wzero[mIns[i].mAddress] && !(mIns[i].mLive & LIVE_CPU_REG_C)) + { + int j = i; + while (j < mIns.Size() && (mIns[j].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_C | LIVE_CPU_REG_Z)) && mIns[j].mType != ASMIT_JSR) + j++; + + if (j < mIns.Size() && mIns[j].mType != ASMIT_JSR) + { + j++; + if (MoveAccuTrainUp(i, j)) + { + i = j + 1; + changed = true; + } + else + i++; + } + else + i++; + } + else if (mIns[i].mType == ASMIT_JSR) + { + wzero.Clear(); + i++; + } + else if (mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].ChangesAddress()) + { + wzero -= mIns[i].mAddress; + i++; + } + else + i++; + } + + CheckLive(); + + if (mTrueJump && mTrueJump->MoveAccuTrainsUp()) + changed = true; + if (mFalseJump && mFalseJump->MoveAccuTrainsUp()) + changed = true; + } + + return changed; +} + bool NativeCodeBasicBlock::AlternateXYUsage(void) { bool changed = false; @@ -13555,6 +13673,62 @@ bool NativeCodeBasicBlock::CanForwardZPMove(int saddr, int daddr, int & index) c return false; } +bool NativeCodeBasicBlock::CanChangeTailZPStoreToX(int addr, const NativeCodeBasicBlock* nblock) const +{ + if (mExitRequiredRegs[CPU_REG_X]) + return false; + + if (mTrueJump && mTrueJump != nblock && mTrueJump->mEntryRequiredRegs[addr]) + return false; + if (mFalseJump && mFalseJump != nblock && mFalseJump->mEntryRequiredRegs[addr]) + return false; + + int i = mIns.Size(); + while (i > 0) + { + i--; + + if (mIns[i].ChangesXReg()) + return false; + + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + return true; + + if (mIns[i].ReferencesZeroPage(addr)) + return false; + } + + if (mEntryBlocks.Size() == 1) + return mEntryBlocks[0]->CanChangeTailZPStoreToX(addr, this); + else + return false; +} + +void NativeCodeBasicBlock::ChangeTailZPStoreToX(int addr) +{ + int i = mIns.Size(); + while (i > 0) + { + i--; + + mIns[i].mLive |= LIVE_CPU_REG_X; + if (mIns[i].mType == ASMIT_STA && mIns[i].mMode == ASMIM_ZERO_PAGE && mIns[i].mAddress == addr) + { + mIns[i].mType = ASMIT_TAX; + mIns[i].mMode = ASMIM_IMPLIED; + return; + } + } + + if (mEntryBlocks.Size() == 1) + { + mEntryBlocks[0]->ChangeTailZPStoreToX(addr); + return; + } + + assert(false); +} + bool NativeCodeBasicBlock::JoinTailCodeSequences(NativeCodeProcedure* proc, bool loops) { @@ -13818,6 +13992,31 @@ bool NativeCodeBasicBlock::JoinTailCodeSequences(NativeCodeProcedure* proc, bool } } #endif +#if 1 + if (mIns.Size() >= 1 && mIns[0].mType == ASMIT_LDA && mIns[0].mMode == ASMIM_ZERO_PAGE && mEntryBlocks.Size() > 1 && !(mIns[0].mLive & LIVE_MEM)) + { + if (mEntryRequiredRegs.Size() > 0 && !mEntryRequiredRegs[CPU_REG_X]) + { + int i = 0; + while (i < mEntryBlocks.Size() && mEntryBlocks[i]->CanChangeTailZPStoreToX(mIns[0].mAddress, this)) + i++; + if (i == mEntryBlocks.Size()) + { + for (int i = 0; i < mEntryBlocks.Size(); i++) + mEntryBlocks[i]->ChangeTailZPStoreToX(mIns[0].mAddress); + mEntryProvidedRegs += CPU_REG_X; + mEntryRequiredRegs += CPU_REG_X; + mIns[0].mType = ASMIT_TXA; + mIns[0].mMode = ASMIM_IMPLIED; + changed = true; + + CheckLive(); + } + } + } + +#endif + #if 1 if (mIns.Size() >= 1 && mIns[0].mType == ASMIT_TAX && !(mIns[0].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_Z))) { @@ -14033,6 +14232,9 @@ bool NativeCodeBasicBlock::JoinTailCodeSequences(NativeCodeProcedure* proc, bool } if (mFalseJump && mFalseJump->mNumEntries == 1 && mTrueJump && !mFalseJump->mFalseJump && mFalseJump->mTrueJump == mTrueJump) { + mTrueJump->CheckLive(); + mFalseJump->CheckLive(); + int s = mIns.Size(), ts = mFalseJump->mIns.Size(); if (s > 1 && ts > 0) { @@ -14045,27 +14247,34 @@ bool NativeCodeBasicBlock::JoinTailCodeSequences(NativeCodeProcedure* proc, bool mIns[s - 1].mType = ASMIT_LDY; mTrueJump->mIns.Insert(0, mIns[s - 2]); mIns.Remove(s - 2); - mFalseJump->mIns.Remove(s - 1); + mFalseJump->mIns.Remove(ts - 1); mFalseJump->mIns.Remove(0); mExitRequiredRegs += CPU_REG_A; mFalseJump->mExitRequiredRegs += CPU_REG_A; mTrueJump->mEntryProvidedRegs += CPU_REG_A; changed = true; + + mTrueJump->CheckLive(); + mFalseJump->CheckLive(); } else if (!(mIns[s - 1].mLive & (LIVE_CPU_REG_A | LIVE_CPU_REG_X)) && HasAsmInstructionMode(ASMIT_LDX, mIns[s - 1].mMode)) { mIns[s - 1].mType = ASMIT_LDX; mTrueJump->mIns.Insert(0, mIns[s - 2]); mIns.Remove(s - 2); - mFalseJump->mIns.Remove(s - 1); + mFalseJump->mIns.Remove(ts - 1); mFalseJump->mIns.Remove(0); mExitRequiredRegs += CPU_REG_A; mFalseJump->mExitRequiredRegs += CPU_REG_A; mTrueJump->mEntryProvidedRegs += CPU_REG_A; changed = true; + + mTrueJump->CheckLive(); + mFalseJump->CheckLive(); } } } + } #endif #if 1 @@ -14099,6 +14308,8 @@ bool NativeCodeBasicBlock::JoinTailCodeSequences(NativeCodeProcedure* proc, bool mFalseJump->mIns.Remove(0); mFalseJump->mIns.Remove(0); CheckLive(); + mTrueJump->CheckLive(); + mFalseJump->CheckLive(); changed = true; } @@ -14132,6 +14343,8 @@ bool NativeCodeBasicBlock::JoinTailCodeSequences(NativeCodeProcedure* proc, bool mFalseJump->mIns.Remove(0); mFalseJump->mIns.Remove(0); CheckLive(); + mTrueJump->CheckLive(); + mFalseJump->CheckLive(); changed = true; } @@ -19719,7 +19932,7 @@ bool NativeCodeBasicBlock::OptimizeInnerLoop(NativeCodeProcedure* proc, NativeCo return false; } -void NativeCodeBasicBlock::CollectInnerLoop(NativeCodeBasicBlock* head, GrowingArray& lblocks) +NativeCodeBasicBlock* NativeCodeBasicBlock::CollectInnerLoop(NativeCodeBasicBlock* head, GrowingArray& lblocks) { if (mLoopHeadBlock != head) { @@ -19729,37 +19942,20 @@ void NativeCodeBasicBlock::CollectInnerLoop(NativeCodeBasicBlock* head, GrowingA if (mTrueJump != head && mFalseJump != head) { if (mTrueJump) - mTrueJump->CollectInnerLoop(head, lblocks); - if (mFalseJump) - mFalseJump->CollectInnerLoop(head, lblocks); + mLoopTailBlock = mTrueJump->CollectInnerLoop(head, lblocks); + + if (mLoopTailBlock && mFalseJump) + { + NativeCodeBasicBlock * tail = mFalseJump->CollectInnerLoop(head, lblocks); + if (tail != mLoopTailBlock) + mLoopTailBlock = nullptr; + } } + else + mLoopTailBlock = this; } -} -NativeCodeBasicBlock* NativeCodeBasicBlock::FindTailBlock(NativeCodeBasicBlock* head) -{ - if (mVisiting || mVisited) - return nullptr; - else if (mTrueJump == head || mFalseJump == head) - return this; - else - { - mVisiting = true; - - NativeCodeBasicBlock* tail = nullptr; - if (mTrueJump) - { - tail = mTrueJump->FindTailBlock(head); - if (tail && mFalseJump && mFalseJump->FindTailBlock(head) != tail) - tail = nullptr; - } - else if (mFalseJump) - tail = mFalseJump->FindTailBlock(head); - - mVisiting = false; - - return tail; - } + return mLoopTailBlock; } bool NativeCodeBasicBlock::OptimizeInnerLoops(NativeCodeProcedure* proc) @@ -19772,19 +19968,12 @@ bool NativeCodeBasicBlock::OptimizeInnerLoops(NativeCodeProcedure* proc) if (mLoopHead) { - NativeCodeBasicBlock* tail = FindTailBlock(this); + GrowingArray lblocks(nullptr); + + NativeCodeBasicBlock* tail = CollectInnerLoop(this, lblocks); if (tail) - { - GrowingArray lblocks(nullptr); - - if (this == tail) - lblocks.Push(this); - else - CollectInnerLoop(this, lblocks); - changed = OptimizeInnerLoop(proc, this, tail, lblocks); - } } CheckLive(); @@ -22476,7 +22665,7 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(NativeCodeProcedure* proc, int pass progress = true; } else if ( - (mIns[i + 0].mType == ASMIT_ASL || mIns[i + 0].mType == ASMIT_LSR || mIns[i + 0].mType == ASMIT_ROL || mIns[i + 0].mType == ASMIT_ROR) && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && + mIns[i + 0].IsShift() && mIns[i + 0].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mType == ASMIT_LDA && mIns[i + 1].mMode == ASMIM_ZERO_PAGE && mIns[i + 1].mAddress == mIns[i + 0].mAddress && !(mIns[i + 1].mLive & LIVE_MEM)) { mIns[i + 1].mType = mIns[i + 0].mType; @@ -26900,6 +27089,11 @@ void NativeCodeProcedure::Optimize(void) } #endif +#if _DEBUG + ResetVisited(); + mEntryBlock->CheckBlocks(); +#endif + #if 1 if (step > 2 && !changed) @@ -27031,6 +27225,14 @@ void NativeCodeProcedure::Optimize(void) if (mEntryBlock->ApplyEntryDataSet()) changed = true; #endif +#if 1 + if (step >= 4) + { + ResetVisited(); + if (mEntryBlock->MoveAccuTrainsUp()) + changed = true; + } +#endif #if 1 if (step == 5) { diff --git a/oscar64/NativeCodeGenerator.h b/oscar64/NativeCodeGenerator.h index fd0ee31..3829f41 100644 --- a/oscar64/NativeCodeGenerator.h +++ b/oscar64/NativeCodeGenerator.h @@ -162,7 +162,7 @@ public: bool mPlaced, mCopied, mKnownShortBranch, mBypassed, mAssembled, mNoFrame, mVisited, mLoopHead, mVisiting, mLocked, mPatched, mPatchFail; NativeCodeBasicBlock * mDominator, * mSameBlock; - NativeCodeBasicBlock* mLoopHeadBlock; + NativeCodeBasicBlock* mLoopHeadBlock, * mLoopTailBlock; NativeRegisterDataSet mDataSet, mNDataSet, mFDataSet; @@ -199,9 +199,8 @@ public: bool OptimizeSelect(NativeCodeProcedure* proc); - NativeCodeBasicBlock* FindTailBlock(NativeCodeBasicBlock* head); bool OptimizeInnerLoops(NativeCodeProcedure* proc); - void CollectInnerLoop(NativeCodeBasicBlock* head, GrowingArray& lblocks); + NativeCodeBasicBlock* CollectInnerLoop(NativeCodeBasicBlock* head, GrowingArray& lblocks); void PutByte(uint8 code); void PutWord(uint16 code); @@ -345,6 +344,9 @@ public: bool HasTailSTA(int& addr, int& index) const; bool PropagateSinglePath(void); + bool CanChangeTailZPStoreToX(int addr, const NativeCodeBasicBlock * nblock) const; + void ChangeTailZPStoreToX(int addr); + bool Check16BitSum(int at, NativeRegisterSum16Info& info); bool Propagate16BitSum(void); @@ -372,6 +374,9 @@ public: bool ExpandADCToBranch(NativeCodeProcedure* proc); bool Split16BitLoopCount(NativeCodeProcedure* proc); + bool MoveAccuTrainUp(int at, int end); + bool MoveAccuTrainsUp(void); + bool AlternateXYUsage(void); bool OptimizeXYPairUsage(void); bool ForwardAbsoluteLoadStores(void); diff --git a/oscar64/oscar64.cpp b/oscar64/oscar64.cpp index d5391cb..35a91f5 100644 --- a/oscar64/oscar64.cpp +++ b/oscar64/oscar64.cpp @@ -74,7 +74,7 @@ int main2(int argc, const char** argv) #else strcpy(strProductName, "oscar64"); - strcpy(strProductVersion, "1.7.146"); + strcpy(strProductVersion, "1.7.148"); #ifdef __APPLE__ uint32_t length = sizeof(basePath); diff --git a/oscar64/oscar64.rc b/oscar64/oscar64.rc index 18fc6c3..c5a8760 100644 --- a/oscar64/oscar64.rc +++ b/oscar64/oscar64.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,7,146,0 - PRODUCTVERSION 1,7,146,0 + FILEVERSION 1,7,148,0 + PRODUCTVERSION 1,7,148,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,12 +43,12 @@ BEGIN BEGIN VALUE "CompanyName", "oscar64" VALUE "FileDescription", "oscar64 compiler" - VALUE "FileVersion", "1.7.146.0" + VALUE "FileVersion", "1.7.148.0" VALUE "InternalName", "oscar64.exe" VALUE "LegalCopyright", "Copyright (C) 2021" VALUE "OriginalFilename", "oscar64.exe" VALUE "ProductName", "oscar64" - VALUE "ProductVersion", "1.7.146.0" + VALUE "ProductVersion", "1.7.148.0" END END BLOCK "VarFileInfo" diff --git a/oscar64setup/oscar64setup.vdproj b/oscar64setup/oscar64setup.vdproj index d11ec7c..be0943c 100644 --- a/oscar64setup/oscar64setup.vdproj +++ b/oscar64setup/oscar64setup.vdproj @@ -100,6 +100,12 @@ } "Entry" { + "MsmKey" = "8:_19321EF0356047C9A6AEE738ECDEB9C3" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_1AAB1F90101E40B6937A64FD4BF9D469" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -310,6 +316,12 @@ } "Entry" { + "MsmKey" = "8:_610F946CE11C4867B87A581F0FFD9030" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_636E67C8267143AA9FC128EB0AF85FC9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -454,6 +466,12 @@ } "Entry" { + "MsmKey" = "8:_8CE64311B70C454D866A7E0098455EB5" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_94AD9BFFA9D04AA0B6BC6EEA1D9A93ED" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -1257,6 +1275,26 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_19321EF0356047C9A6AEE738ECDEB9C3" + { + "SourcePath" = "8:..\\samples\\sprites\\sprmux32.c" + "TargetName" = "8:sprmux32.c" + "Tag" = "8:" + "Folder" = "8:_A891BBCA276B4516852A6BFD884353E3" + "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}:_1AAB1F90101E40B6937A64FD4BF9D469" { "SourcePath" = "8:..\\include\\setjmp.c" @@ -1957,6 +1995,26 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_610F946CE11C4867B87A581F0FFD9030" + { + "SourcePath" = "8:..\\samples\\resources\\ballsprite.bin" + "TargetName" = "8:ballsprite.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}:_636E67C8267143AA9FC128EB0AF85FC9" { "SourcePath" = "8:..\\include\\c64\\kernalio.h" @@ -2437,6 +2495,26 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8CE64311B70C454D866A7E0098455EB5" + { + "SourcePath" = "8:..\\samples\\sprites\\sprmux64.c" + "TargetName" = "8:sprmux64.c" + "Tag" = "8:" + "Folder" = "8:_A891BBCA276B4516852A6BFD884353E3" + "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}:_94AD9BFFA9D04AA0B6BC6EEA1D9A93ED" { "SourcePath" = "8:..\\bin\\oscar64.bat" @@ -4153,15 +4231,15 @@ { "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:oscar64" - "ProductCode" = "8:{4CED0CCC-42E5-4E78-8322-D85C2511864C}" - "PackageCode" = "8:{51B4E6A6-575E-4C5C-A8C9-18278CCFEB43}" + "ProductCode" = "8:{142E4BF0-B9DB-47DB-9EA2-FF3404EFD5D0}" + "PackageCode" = "8:{C2449794-2B3A-435B-9211-ED0ED63EAEB7}" "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.7.146" + "ProductVersion" = "8:1.7.148" "Manufacturer" = "8:oscar64" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:" diff --git a/samples/resources/ballsprite.bin b/samples/resources/ballsprite.bin new file mode 100644 index 0000000..5324ca8 Binary files /dev/null and b/samples/resources/ballsprite.bin differ diff --git a/samples/sprites/build.sh b/samples/sprites/build.sh index 8ac1784..d1c2a9e 100644 --- a/samples/sprites/build.sh +++ b/samples/sprites/build.sh @@ -2,3 +2,5 @@ ../../bin/oscar64 joycontrol.c ../../bin/oscar64 multiplexer.c -n ../../bin/oscar64 creditroll.c -n +../../bin/oscar64 -n sprmux32.c -O2 -dVSPRITES_MAX=32 -dNUM_IRQS=28 +../../bin/oscar64 -n sprmux64.c diff --git a/samples/sprites/make.bat b/samples/sprites/make.bat index 7f6747e..c45ab2a 100644 --- a/samples/sprites/make.bat +++ b/samples/sprites/make.bat @@ -1,3 +1,5 @@ call ..\..\bin\oscar64 joycontrol.c call ..\..\bin\oscar64 multiplexer.c -n call ..\..\bin\oscar64 creditroll.c -n +call ..\..\bin\oscar64 -n sprmux32.c -O2 -dVSPRITES_MAX=32 -dNUM_IRQS=28 +call ..\..\bin\oscar64 -n sprmux64.c diff --git a/samples/sprites/sprmux32.c b/samples/sprites/sprmux32.c new file mode 100644 index 0000000..965cd57 --- /dev/null +++ b/samples/sprites/sprmux32.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include + +static const sbyte sintab[256] = { + 0, 2, 4, 7, 9, 11, 13, 15, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 55, 57, 59, 60, + 62, 64, 65, 67, 68, 70, 71, 72, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 85, 86, 87, 87, 88, 88, 89, 89, 89, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 89, 89, 89, 88, 88, 87, 87, 86, 85, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 72, 71, 70, 68, 67, 65, 64, + 62, 60, 59, 57, 55, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, 30, 28, 26, 24, 22, 20, 18, 15, 13, 11, 9, 7, 4, 2, + 0, -2, -4, -7, -9, -11, -13, -15, -18, -20, -22, -24, -26, -28, -30, -32, -34, -36, -38, -40, -42, -44, -46, -48, -50, -52, -54, -55, -57, -59, -60, + -62, -64, -65, -67, -68, -70, -71, -72, -74, -75, -76, -77, -78, -79, -80, -81, -82, -83, -84, -85, -85, -86, -87, -87, -88, -88, -89, -89, -89, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -89, -89, -89, -88, -88, -87, -87, -86, -85, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -72, -71, -70, -68, -67, -65, -64, + -62, -60, -59, -57, -55, -54, -52, -50, -48, -46, -44, -42, -40, -38, -36, -34, -32, -30, -28, -26, -24, -22, -20, -18, -15, -13, -11, -9, -7, -4, -2 +}; + +static const sbyte costab[256] = { + 120, 120, 120, 120, 119, 119, 119, 118, 118, 117, 116, 116, 115, 114, 113, 112, 111, 110, 108, 107, 106, 104, 103, 101, 100, 98, 96, 95, 93, 91, 89, 87, + 85, 83, 81, 78, 76, 74, 71, 69, 67, 64, 62, 59, 57, 54, 51, 49, 46, 43, 40, 38, 35, 32, 29, 26, 23, 21, 18, 15, 12, 9, 6, 3, + 0, -3, -6, -9, -12, -15, -18, -21, -23, -26, -29, -32, -35, -38, -40, -43, -46, -49, -51, -54, -57, -59, -62, -64, -67, -69, -71, -74, -76, -78, -81, -83, + -85, -87, -89, -91, -93, -95, -96, -98, -100, -101, -103, -104, -106, -107, -108, -110, -111, -112, -113, -114, -115, -116, -116, -117, -118, -118, -119, -119, -119, -120, -120, -120, + -120, -120, -120, -120, -119, -119, -119, -118, -118, -117, -116, -116, -115, -114, -113, -112, -111, -110, -108, -107, -106, -104, -103, -101, -100, -98, -96, -95, -93, -91, -89, -87, + -85, -83, -81, -78, -76, -74, -71, -69, -67, -64, -62, -59, -57, -54, -51, -49, -46, -43, -40, -38, -35, -32, -29, -26, -23, -21, -18, -15, -12, -9, -6, -3, + 0, 3, 6, 9, 12, 15, 18, 21, 23, 26, 29, 32, 35, 38, 40, 43, 46, 49, 51, 54, 57, 59, 62, 64, 67, 69, 71, 74, 76, 78, 81, 83, + 85, 87, 89, 91, 93, 95, 96, 98, 100, 101, 103, 104, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 116, 117, 118, 118, 119, 119, 119, 120, 120, 120 +}; + +#define Screen ((char *)0x400) + +// make space until 0x2000 for code and data + +#pragma region( lower, 0x0a00, 0x2000, , , {code, data} ) + +// then space for our sprite data + +#pragma section( spriteset, 0) + +#pragma region( spriteset, 0x2000, 0x2800, , , {spriteset} ) + +// everything beyond will be code, data, bss and heap to the end + +#pragma region( main, 0x2800, 0xa000, , , {code, data, bss, heap, stack} ) + + +// spriteset at fixed location + +#pragma data(spriteset) + +static const char spriteset[2048] = { + #embed "../resources/digitsprites.bin" +}; + +#pragma data(data) + + +int main(void) +{ + // Disable interrupts while setting up + __asm { sei }; + + // Kill CIA interrupts + cia_init(); + + mmap_set(MMAP_NO_ROM); + + // enable raster interrupt via direct path + rirq_init(false); + + // initialize sprite multiplexer + vspr_init(Screen); + + // initalize sprites + for(char i=0; i<32; i++) + { + vspr_set(i, 30 + 8 * i, 220 - 4 * i, (unsigned)&(spriteset[0]) / 64 + (i & 15), (i & 7) + 8); + } + + // initial sort and update + vspr_sort(); + vspr_update(); + rirq_sort(); + + // start raster IRQ processing + rirq_start(); + + // Black screen + vic.color_border = 0; + vic.color_back = 0; + + // animation loop + unsigned j = 0, t = 0; + unsigned k = 91; + for(;;) + { + // Use MSB as start position + j = t << 8; + + // Unroll movement loop for performance + #pragma unroll(full) + for(char i=0; i<32; i++) + { + vspr_move(i, 170 + costab[((j >> 8) + 8 * i) & 255], 140 + sintab[(t + 8 * i) & 255]); + j += k; + } + + // Advance animation + t+=3; + k++; + + // sort virtual sprites by y position + vspr_sort(); + + // wait for raster IRQ to reach and of frame + rirq_wait(); + + // update sprites back to normal and set up raster IRQ for sprites 8 to 31 + vspr_update(); + + // sort raster IRQs + rirq_sort(); + + } + + return 0; +} diff --git a/samples/sprites/sprmux32.png b/samples/sprites/sprmux32.png new file mode 100644 index 0000000..20af3b5 Binary files /dev/null and b/samples/sprites/sprmux32.png differ diff --git a/samples/sprites/sprmux64.c b/samples/sprites/sprmux64.c new file mode 100644 index 0000000..94cba11 --- /dev/null +++ b/samples/sprites/sprmux64.c @@ -0,0 +1,178 @@ +#include +#include +#include +#include + +static const sbyte costab[256] = { + 30, 30, 30, 30, 30, 30, 30, 30, 29, 29, 29, 29, 29, 28, 28, 28, 28, 27, 27, 27, 26, 26, 26, 25, 25, 25, 24, 24, 23, 23, 22, 22, + 21, 21, 20, 20, 19, 18, 18, 17, 17, 16, 15, 15, 14, 13, 13, 12, 11, 11, 10, 9, 9, 8, 7, 7, 6, 5, 4, 4, 3, 2, 1, 1, + 0, -1, -1, -2, -3, -4, -4, -5, -6, -7, -7, -8, -9, -9, -10, -11, -11, -12, -13, -13, -14, -15, -15, -16, -17, -17, -18, -18, -19, -20, -20, + -21, -21, -22, -22, -23, -23, -24, -24, -25, -25, -25, -26, -26, -26, -27, -27, -27, -28, -28, -28, -28, -29, -29, -29, -29, -29, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30, -29, -29, -29, -29, -29, -28, -28, -28, -28, -27, -27, -27, -26, -26, -26, -25, -25, -25, -24, -24, -23, -23, -22, -22, + -21, -21, -20, -20, -19, -18, -18, -17, -17, -16, -15, -15, -14, -13, -13, -12, -11, -11, -10, -9, -9, -8, -7, -7, -6, -5, -4, -4, -3, -2, -1, -1, + 0, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18, 18, 19, 20, 20, + 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26, 26, 26, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30 +}; + +#define Screen ((char *)0x400) + +// make space until 0x2000 for code and data + +#pragma region( lower, 0x0a00, 0x2000, , , {code, data} ) + +// then space for our sprite data + +#pragma section( spriteset, 0) + +#pragma region( spriteset, 0x2000, 0x2040, , , {spriteset} ) + +// everything beyond will be code, data, bss and heap to the end + +#pragma region( main, 0x2800, 0xa000, , , {code, data, bss, heap, stack} ) + + +// spriteset at fixed location + +#pragma data(spriteset) + +__export const char spriteset[64] = { + #embed "../resources/ballsprite.bin" +}; + +#pragma data(data) + +// Control variables for the interrupt + +char pphase; // global phase +char poffset; // phase offset for this row of sprites +char yoffset; // vertical offset for this row of sprites + +// Interrupt routine switching to next row of sprites, invokes some lines after +// start of previous row + +__interrupt void setspr(void) +{ + // Switch vertical position first, will not take effect + // until after the current row of sprites is complete, so it + // is done first + #pragma unroll(full) + for(char i=0; i<8; i++) + vic.spr_pos[i].y = yoffset; + + char phase = pphase + poffset; // Effective rotation phase for this row + int step = costab[phase]; // 30 * Cosine of phase for x step + int xpos0 = 172 - (step >> 1); // Half a step to the left + int xpos1 = 172 + (step >> 1); // Half a step to the right + + // Table for xpositions + static unsigned xp[8]; + + // Calculate xpositions, four to the left and four to the right + #pragma unroll(full) + for(char i=0; i<4; i++) + { + xp[3 - i] = xpos0; + xp[i + 4] = xpos1; + + // Stepping left and right + xpos0 -= step; + xpos1 += step; + } + + // Wait for end of current sprite, xpos will take effect + // at start of line, so we need to patch it after the last + // pixel line has started + vic_waitLine(yoffset - 4); + + // Left to right or right to left to get a matching z order + if (phase & 0x80) + { + // MSB mask + char xymask = 0; + + // Update all sprite x LSB and color, put MSB into + // xymask bit + #pragma unroll(full) + for(char i=0; i<8; i++) + { + xymask = ((unsigned)xymask | (xp[i] & 0xff00)) >> 1; + vic.spr_pos[i].x = xp[i]; + vic.spr_color[i] = VCOL_ORANGE + i; + } + + // Update MSB + vic.spr_msbx = xymask; + } + else + { + char xymask = 0; + + // Update all sprite x LSB and color, put MSB into + // xymask bit + + #pragma unroll(full) + for(char i=0; i<8; i++) + { + xymask = ((unsigned)xymask | (xp[7 - i] & 0xff00)) >> 1; + vic.spr_pos[i].x = xp[7 - i]; + vic.spr_color[i] = VCOL_ORANGE + (7 - i); + } + + // Update MSB + vic.spr_msbx = xymask; + } +} + +// Eight raster interrupts +RIRQCode spmux[8]; + +int main(void) +{ + // Keep kernal alive + rirq_init(true); + + // Setup sprite images + for(char i=0; i<8; i++) + Screen[0x03f8 + i] = 128; + + // Remaining sprite registers + vic.spr_enable = 0xff; + vic.spr_multi = 0xff; + vic.spr_expand_x = 0x00; + vic.spr_expand_y = 0x00; + vic.spr_mcolor0 = VCOL_BLACK; + vic.spr_mcolor1 = VCOL_WHITE; + + // Setup raster interrupts + for(char i=0; i<8; i++) + { + // Three operations per interrupt + rirq_build(spmux + i, 3); + // Store phase offset + rirq_write(spmux + i, 0, &poffset, 11 * i); + // Store vertical position + rirq_write(spmux + i, 1, &yoffset, 52 + 25 * i); + // Call sprite update function + rirq_call(spmux + i, 2, setspr); + + // Place raster interrupt 16 lines before sprite start to + // give it enough time for procesing + rirq_set(i, 36 + 25 * i, spmux + i); + } + + // Sort interrupts and start processing + rirq_sort(); + rirq_start(); + + // Forever + for(;;) + { + // Advance phase + pphase += 5; + + // Wait for next frame + vic_waitFrame(); + } + + return 0; +} diff --git a/samples/sprites/sprmux64.png b/samples/sprites/sprmux64.png new file mode 100644 index 0000000..afcd4d2 Binary files /dev/null and b/samples/sprites/sprmux64.png differ