From 864c8ec9a5e221d175f1882d797e91c9e8457761 Mon Sep 17 00:00:00 2001 From: drmortalwombat <90205530+drmortalwombat@users.noreply.github.com> Date: Mon, 18 Jul 2022 21:22:12 +0200 Subject: [PATCH] Add samples for 32 and 64 sprite multiplexing --- README.md | 18 +- include/c64/sprites.c | 10 ++ include/c64/sprites.h | 11 +- include/c64/vic.c | 5 + include/c64/vic.h | 3 + oscar64/NativeCodeGenerator.cpp | 286 ++++++++++++++++++++++++++----- oscar64/NativeCodeGenerator.h | 11 +- oscar64/oscar64.cpp | 2 +- oscar64/oscar64.rc | 8 +- oscar64setup/oscar64setup.vdproj | 84 ++++++++- samples/resources/ballsprite.bin | Bin 0 -> 64 bytes samples/sprites/build.sh | 2 + samples/sprites/make.bat | 2 + samples/sprites/sprmux32.c | 126 ++++++++++++++ samples/sprites/sprmux32.png | Bin 0 -> 5911 bytes samples/sprites/sprmux64.c | 178 +++++++++++++++++++ samples/sprites/sprmux64.png | Bin 0 -> 7328 bytes 17 files changed, 690 insertions(+), 56 deletions(-) create mode 100644 samples/resources/ballsprite.bin create mode 100644 samples/sprites/sprmux32.c create mode 100644 samples/sprites/sprmux32.png create mode 100644 samples/sprites/sprmux64.c create mode 100644 samples/sprites/sprmux64.png 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 0000000000000000000000000000000000000000..5324ca86285cef77263f9061c90984ccdf1be3ba GIT binary patch literal 64 zcmZPwWnj#7V4D@dwrYaZsuZzTQ>1<_$$m96>(|n(SD9I>mPW0bnz<50ip>g +#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 0000000000000000000000000000000000000000..20af3b5338230d18f568838a9c0b102e700aece8 GIT binary patch literal 5911 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..afcd4d29e74dda6b03a8a85f4606217a42079628 GIT binary patch literal 7328 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 ZDPkou{=m}f