From 94cbae7a844c8d7c79b0555a7b78e284efaefd1c Mon Sep 17 00:00:00 2001 From: thrust26 Date: Sun, 12 Apr 2020 11:35:41 +0200 Subject: [PATCH] improve CartEnhanced to allow swapped RAM read/write ports refactor CartCV add more CV test ROMs --- src/emucore/CartCV.cxx | 160 +++--------------- src/emucore/CartCV.hxx | 82 ++------- src/emucore/CartEnhanced.cxx | 33 ++-- src/emucore/CartEnhanced.hxx | 20 ++- .../roms/bankswitching/CV/ColorGotcha2600.bin | Bin 0 -> 2048 bytes ... Character Set $f100 (1982) (CommaVid).a26 | Bin 0 -> 4096 bytes ...m 2 - Memo Pad $f100 (1982) (CommaVid).a26 | Bin 0 -> 4096 bytes ...arget Practice $f100 (1982) (CommaVid).a26 | Bin 0 -> 4096 bytes ...ur Own Display $f200 (1982) (CommaVid).a26 | Bin 0 -> 4096 bytes ...ogram 5 - Life $f15e (1982) (CommaVid).a26 | Bin 0 -> 4096 bytes .../CV/Video Life (CommaVid) [h1].a26 | Bin 4096 -> 0 bytes 11 files changed, 72 insertions(+), 223 deletions(-) create mode 100644 test/roms/bankswitching/CV/ColorGotcha2600.bin create mode 100644 test/roms/bankswitching/CV/Magicard Sample Program 1 - Display of Character Set $f100 (1982) (CommaVid).a26 create mode 100644 test/roms/bankswitching/CV/Magicard Sample Program 2 - Memo Pad $f100 (1982) (CommaVid).a26 create mode 100644 test/roms/bankswitching/CV/Magicard Sample Program 3 - Target Practice $f100 (1982) (CommaVid).a26 create mode 100644 test/roms/bankswitching/CV/Magicard Sample Program 4 - Generating Your Own Display $f200 (1982) (CommaVid).a26 create mode 100644 test/roms/bankswitching/CV/Magicard Sample Program 5 - Life $f15e (1982) (CommaVid).a26 delete mode 100644 test/roms/bankswitching/CV/Video Life (CommaVid) [h1].a26 diff --git a/src/emucore/CartCV.cxx b/src/emucore/CartCV.cxx index 42c74a808..136e817c3 100644 --- a/src/emucore/CartCV.cxx +++ b/src/emucore/CartCV.cxx @@ -21,164 +21,42 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeCV::CartridgeCV(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5), - mySize(size) + : CartridgeEnhanced(image, size, md5, settings) { - if(mySize == myImage.size()) - { - // Copy the ROM data into my buffer - std::copy_n(image.get(), myImage.size(), myImage.begin()); - } - else if(mySize == 4_KB) + myBankShift = BANK_SHIFT; + myRamSize = RAM_SIZE; + myRamWpHigh = RAM_HIGH_WP; + + + if(mySize == 4_KB) { // The game has something saved in the RAM // Useful for MagiCard program listings - // Copy the ROM data into my buffer - std::copy_n(image.get() + myImage.size(), myImage.size(), myImage.begin()); + // Allocate array for the ROM image + mySize = 2_KB; + myImage = make_unique(mySize); + // Copy the ROM image into my buffer + std::copy_n(image.get() + mySize, mySize, myImage.get()); + + + myInitialRAM = make_unique(1_KB); // Copy the RAM image into a buffer for use in reset() - std::copy_n(image.get(), myInitialRAM.size(), myInitialRAM.begin()); + std::copy_n(image.get(), 1_KB, myInitialRAM.get()); } - createRomAccessArrays(myImage.size() + myRAM.size()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeCV::reset() { - if(mySize == 4_KB) + if(myInitialRAM != nullptr) { // Copy the RAM image into my buffer - myRAM = myInitialRAM; + std::copy_n(myInitialRAM.get(), 1_KB, myRAM.get()); } else - initializeRAM(myRAM.data(), myRAM.size()); + initializeRAM(myRAM.get(), myRamSize); myBankChanged = true; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeCV::install(System& system) -{ - mySystem = &system; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Map ROM image into the system - for(uInt16 addr = 0x1800; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[addr & 0x07FF]; - access.romAccessBase = &myRomAccessBase[addr & 0x07FF]; - access.romPeekCounter = &myRomAccessCounter[addr & 0x07FF]; - access.romPokeCounter = &myRomAccessCounter[(addr & 0x07FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Set the page accessing method for the RAM writing pages - // Map access to this class, since we need to inspect all accesses to - // check if RWP happens - access.directPeekBase = nullptr; - access.romAccessBase = nullptr; - access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE) - mySystem->setPageAccess(addr, access); - - // Set the page accessing method for the RAM reading pages - access.directPokeBase = nullptr; - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1000; addr < 0x1400; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myRAM[addr & 0x03FF]; - access.romAccessBase = &myRomAccessBase[2048 + (addr & 0x03FF)]; - access.romPeekCounter = &myRomAccessCounter[2048 + (addr & 0x03FF)]; - access.romPokeCounter = &myRomAccessCounter[2048 + (addr & 0x03FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeCV::peek(uInt16 address) -{ - // The only way we can get to this method is if we attempt to read from - // the write port (0xF400 - 0xF7FF, 1024 bytes), in which case an - // unwanted write is potentially triggered - return peekRAM(myRAM[address & 0x03FF], address); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCV::poke(uInt16 address, uInt8 value) -{ - - if(address & 0x0400) - { - pokeRAM(myRAM[address & 0x03FF], address, value); - return true; - } - else - { - // Writing to the read port should be ignored, but trigger a break if option enabled - uInt8 dummy; - - pokeRAM(dummy, address, value); - myRamWriteAccess = address; - return false; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCV::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0800) - { - // Normally, a write to the read port won't do anything - // However, the patch command is special in that ignores such - // cart restrictions - // The following will work for both reads and writes - myRAM[address & 0x03FF] = value; - } - else - myImage[address & 0x07FF] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeCV::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCV::save(Serializer& out) const -{ - try - { - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeCV::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCV::load(Serializer& in) -{ - try - { - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeCV::load" << endl; - return false; - } - - return true; -} diff --git a/src/emucore/CartCV.hxx b/src/emucore/CartCV.hxx index 3b872ce9d..4e18040a6 100644 --- a/src/emucore/CartCV.hxx +++ b/src/emucore/CartCV.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartCVWidget.hxx" #endif @@ -33,9 +33,9 @@ class System; $F400-$F7FF write to RAM $F800-$FFFF ROM - @author Eckhard Stolberg + @author Eckhard Stolberg, Thomas Jentzsch */ -class CartridgeCV : public Cartridge +class CartridgeCV : public CartridgeEnhanced { friend class CartridgeCVWidget; @@ -58,47 +58,6 @@ class CartridgeCV : public Cartridge */ void reset() override; - /** - Install cartridge in the specified system. Invoked by the system - when the cartridge is attached to it. - - @param system The system the device should install itself in - */ - void install(System& system) override; - - /** - Patch the cartridge ROM. - - @param address The ROM address to patch - @param value The value to place into the address - @return Success or failure of the patch operation - */ - bool patch(uInt16 address, uInt8 value) override; - - /** - Access the internal ROM image for this cartridge. - - @param size Set to the size of the internal ROM image data - @return A pointer to the internal ROM image data - */ - const uInt8* getImage(size_t& size) const override; - - /** - Save the current state of this cart to the given Serializer. - - @param out The Serializer object to use - @return False on any errors, else true - */ - bool save(Serializer& out) const override; - - /** - Load the current state of this cart from the given Serializer. - - @param in The Serializer object to use - @return False on any errors, else true - */ - bool load(Serializer& in) override; - /** Get a descriptor for the device name (used in error checking). @@ -118,35 +77,22 @@ class CartridgeCV : public Cartridge } #endif - public: - /** - Get the byte at the specified address + private: + bool checkSwitchBank(uInt16, uInt8 = 0) override { return false; }; - @return The byte at the specified address - */ - uInt8 peek(uInt16 address) override; - - /** - Change the byte at the specified address to the given value - - @param address The address where the value should be stored - @param value The value to be stored at the address - @return True if the poke changed the device address space, else false - */ - bool poke(uInt16 address, uInt8 value) override; + protected: + // Initial RAM data from the cart (doesn't always exist) + ByteBuffer myInitialRAM{nullptr}; private: - // The 2k ROM image for the cartridge - std::array myImage; + // Calculated as: log(ROM bank segment size) / log(2) + static constexpr uInt16 BANK_SHIFT = 11; // 2K - // Initial size of the cart data - size_t mySize{0}; + // RAM size + static constexpr uInt16 RAM_SIZE = 0x400; // 1K - // The 1024 bytes of RAM - std::array myRAM; - - // Initial RAM data from the cart (doesn't always exist) - std::array myInitialRAM; + // Write port for extra RAM is at high address + static constexpr bool RAM_HIGH_WP = true; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartEnhanced.cxx b/src/emucore/CartEnhanced.cxx index 6752d3153..c479ddd92 100644 --- a/src/emucore/CartEnhanced.cxx +++ b/src/emucore/CartEnhanced.cxx @@ -42,6 +42,8 @@ void CartridgeEnhanced::install(System& system) myBankMask = myBankSize - 1; // e.g. = 0x0FFF myBankSegs = 1 << (12 - myBankShift); // e.g. = 1 myRamMask = myRamSize - 1; // e.g. = 0xFFFF (doesn't matter for RAM size 0) + myWriteOffset = myRamWpHigh ? myRamSize : 0; + myReadOffset = myRamWpHigh ? 0 : myRamSize; // Allocate array for the current bank segments slices myCurrentSegOffset = make_unique(myBankSegs); @@ -58,24 +60,24 @@ void CartridgeEnhanced::install(System& system) // Map access to this class, since we need to inspect all accesses to // check if RWP happens access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1000; addr < 0x1000 + myRamSize; addr += System::PAGE_SIZE) + for(uInt16 addr = 0x1000 + myWriteOffset; addr < 0x1000 + myWriteOffset + myRamSize; addr += System::PAGE_SIZE) { uInt16 offset = addr & myRamMask; - access.romAccessBase = &myRomAccessBase[offset]; - access.romPeekCounter = &myRomAccessCounter[offset]; - access.romPokeCounter = &myRomAccessCounter[offset + myAccessSize]; + access.romAccessBase = &myRomAccessBase[myWriteOffset + offset]; + access.romPeekCounter = &myRomAccessCounter[myWriteOffset + offset]; + access.romPokeCounter = &myRomAccessCounter[myWriteOffset + offset + myAccessSize]; mySystem->setPageAccess(addr, access); } // Set the page accessing method for the RAM reading pages access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1000 + myRamSize; addr < 0x1000 + myRamSize * 2; addr += System::PAGE_SIZE) + for(uInt16 addr = 0x1000 + myReadOffset; addr < 0x1000 + myReadOffset + myRamSize; addr += System::PAGE_SIZE) { uInt16 offset = addr & myRamMask; access.directPeekBase = &myRAM[offset]; - access.romAccessBase = &myRomAccessBase[myRamSize + offset]; - access.romPeekCounter = &myRomAccessCounter[myRamSize + offset]; - access.romPokeCounter = &myRomAccessCounter[myRamSize + offset + myAccessSize]; + access.romAccessBase = &myRomAccessBase[myReadOffset + offset]; + access.romPeekCounter = &myRomAccessCounter[myReadOffset + offset]; + access.romPokeCounter = &myRomAccessCounter[myReadOffset + offset + myAccessSize]; mySystem->setPageAccess(addr, access); } @@ -106,7 +108,9 @@ uInt8 CartridgeEnhanced::peek(uInt16 address) checkSwitchBank(address & 0x0FFF); address &= myBankMask; - if(address < myRamSize) // Write port is at 0xF000 - 0xF07F (128 bytes) + // Write port is e.g. at 0xF000 - 0xF07F (128 bytes) + if(address < myReadOffset + myRamSize && address >= myReadOffset) + // This is a read accees to a write port! return peekRAM(myRAM[address], peekAddress); else return myImage[myCurrentSegOffset[(peekAddress & 0xFFF) >> myBankShift] + address]; @@ -115,15 +119,18 @@ uInt8 CartridgeEnhanced::peek(uInt16 address) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeEnhanced::poke(uInt16 address, uInt8 value) { + uInt16 pokeAddress = address; + // Switch banks if necessary if (checkSwitchBank(address & 0x0FFF, value)) return false; + address &= myBankMask; if(myRamSize) { - if(!(address & myRamSize)) + if(bool(address & myRamSize) == myRamWpHigh) { - pokeRAM(myRAM[address & myRamMask], address, value); + pokeRAM(myRAM[address & myRamMask], pokeAddress, value); return true; } else @@ -131,8 +138,8 @@ bool CartridgeEnhanced::poke(uInt16 address, uInt8 value) // Writing to the read port should be ignored, but trigger a break if option enabled uInt8 dummy; - pokeRAM(dummy, address, value); - myRamWriteAccess = address; + pokeRAM(dummy, pokeAddress, value); + myRamWriteAccess = pokeAddress; } } return false; diff --git a/src/emucore/CartEnhanced.hxx b/src/emucore/CartEnhanced.hxx index 6437c362e..6c67b6eaf 100644 --- a/src/emucore/CartEnhanced.hxx +++ b/src/emucore/CartEnhanced.hxx @@ -40,7 +40,7 @@ class CartridgeEnhanced : public Cartridge @param settings A reference to the various settings (read-only) */ CartridgeEnhanced(const ByteBuffer& image, size_t size, const string& md5, - const Settings& settings); + const Settings& settings); virtual ~CartridgeEnhanced() = default; public: @@ -160,6 +160,21 @@ class CartridgeEnhanced : public Cartridge // The mask for the extra RAM uInt16 myRamMask{0}; // RAM_SIZE - 1, but doesn't matter when RAM_SIZE is 0 + // The offset into ROM space for writing to RAM + // - xxSC = 0x0000 + // - FA(2) = 0x0000 + // - CV = 0x0400 + uInt16 myWriteOffset{0}; + + // The offset into ROM space for reading from RAM + // - xxSC = 0x0080 + // - FA(2) = 0x0100 + // - CV = 0x0000 + uInt16 myReadOffset{0}; + + // Flag, true if write port is at high and read port is at low address + bool myRamWpHigh{RAM_HIGH_WP}; + // Pointer to a dynamically allocated ROM image of the cartridge ByteBuffer myImage{nullptr}; @@ -182,6 +197,9 @@ class CartridgeEnhanced : public Cartridge // The size of extra RAM in ROM address space static constexpr uInt16 RAM_SIZE = 0; // default = none + // Write port for extra RAM is at low address by default + static constexpr bool RAM_HIGH_WP = false; + protected: /** Check hotspots and switch bank if triggered. diff --git a/test/roms/bankswitching/CV/ColorGotcha2600.bin b/test/roms/bankswitching/CV/ColorGotcha2600.bin new file mode 100644 index 0000000000000000000000000000000000000000..318a2d41dbf669d758477bbc9a46ca8f19c7df76 GIT binary patch literal 2048 zcmds0O>7fa5PrLM{72$VN+^j)=|Yntm(T`;XoUC)RVYci2ZWjn?O{=ymPknDQaOhr zx$aKvm^e+zZg6rkRvzRL(Gpcw#Q}**B~8+UmkWYJC0`POFI@ye{vmN^<6R#>9DC`k zwKMb0&zt?`nd=1pv2gQ6uiJ+rKS+_-?DBMvUShQ{!p0!N4EUZA2%-LgeFEXtuz0G( z{J;b0(+g~Oe3D;{(plXlUdW4|6~%#q__QGUisHQ@`xrb}>Sag4&3*wF+PZIg^d)NZ`f8 zlLf>t%I(Jt7{L_;?-G@3m+QpW}dZgzFozwSWUruko z5#nci%K^jiBtS_K@#g~Kjuel$xr0i~m4IQ4Fs?n!8#OYm?RX{$`6e7eb*VQ!>B!QU z!9R~QSCoRs0kM$n{evD3e1c3!G$L|&s0Wdbm_`Haz-7KC(@|s7 z!f4;%d~X>p>OM9Bqco0ooW}KS%ng@teG_aQeq^U$jFsUhCc(ILx`^&2G%F#u6(TS$ zCy8=dgkqAzV$;C!j_R9drtVA`-u%X6c?RKIZ5T>Tds&;kKw2kIzV!D%ye_Za!;&{_=MMkc>k?Ssc-Kbf(E;W}ZTk_WL zWgcKOa(CpWn~tnRq_rZAtc-w4LxU1%cf$yWS2U@yM75RhfZ7MwQ)Gr2fm?r-K{a+R zmG#;v+S(TL4Qf}% z{fXO-^{Ox5P_mile zO*)Y`AMorB)Zr@pAVl>iI0Dj#pc3~IB;T(HN{n?uU5Y4Pq~wiC5A&T%DQv$))<(Sq z^Q~iNV)G%`hF!e#VQ4Y+qijNV#-wl8Vt8+zt1;byrMaY{|4IHY68Df{FI8dN{22JD z1}!Oa>({kmFL>t1LCED6F0RI^jDcs^o68|fDl(*1GZ(C76v1Y>jp#&a_< zi{g#qsyFPhmymRt31-Ka)|hl7K%lO&)u-s-1xewa9%bLs3(|r7Vnq^`UOau^b;v6z zRlqjRpFB^*^CIzBLb@t4G-MmEMC{EE4ZU1rl~h!ZU0BVtTXqraIJ<$zKCL>29kI{) z?=)ADjD12T(_4{@72&u{b#=5`Ct zj*wt~e}8bNDZ%bwLlA%XG$4w)I~eRYQ54fc2m?XV)^>cL?RagO>^OGji!TSh`ugnP Nxo^1gYedMK&%ZpZ@(KU| literal 0 HcmV?d00001 diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 1 - Display of Character Set $f100 (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 1 - Display of Character Set $f100 (1982) (CommaVid).a26 new file mode 100644 index 0000000000000000000000000000000000000000..6eafcbe511a3d16cc2d1740b63bfb71079f91999 GIT binary patch literal 4096 zcmeHK|8E-S8Gkv>7@xmz&gzv&*>ug))Ue8;YbQBfqZ&9OxL#~(SO)12W2(I2Dr+{W zk~e8h0sAHH+1Pvu5os6ga`Jf`YCK!2ySkdCY+5Qnop$Y3DU~*rc}d2slw1}&N^I=g zd!T6l!|FpkpZD{;ywB%(p7+(OLsh@rj|394Kmc4eSFI58eX3Yrm<83DSx{+Jx&>ff z&W#eyxlPt|hdRB*_TBk%cWkfvy$V0Z zBMYQ|HF02I01OZXz=Odj>G&Yc;DjMY({v2b>oodF1vdg}AcE5j!_c*Zwcgq~S39^? z>+Q4l)e1FhAD{sp!LA#xW5q)b++A6yE$|)0E$-G@tj5)9`C4z4AqE*H78A+~`D#AD z06=wNy=#4;+8df=twp?RZf=d|3+r<{&(O@P4%+?d6*uj;f)fD0?qOLw?X(Zlb|33- zIvq}*!}X%W<98hQI0jt~FY7qrbe!-xk9(cofYTeKT~61~uxHT89QQK5VV1VDbbzIU ztZ#T|*gibOBJiOoe!I^f@cDyU*x?tPtkcQTPS)dOPtdLa?FxDZ1D-)`)!>lV>$ZE{ zUN39+vfgHdVZUeC@AJ8wJ{RqCdHnW(-yZb)0{(!#8I|%-RKP+FpQKI(P6jQ(UJGTX z0#xww1SW4o4C){OJ7E_zi5!fHQ*c~T6HyFua7wBrBA5UaN}M9iA5MwSk{!r?hcqC2 z)Mk)W*A$%U?~7nO4zX3B5a@xOt5yYet@;&cLM}wlXf&@(LX-SiRRWdAkY+>AW~f+6 ztL+b*rB%6T(aS3$hh#XhAIWgy5hTNj&oyK?$gv-62_(eI^0s7cBn=W-OwcWJwns|s zHbTZFpkPvamisg&W0IPUmC1izj$zEhD#4--NQV;VvC#a?a?bKb|Gz2A^Dxc87(tjv zVlXB>K$U~}nM#Y&Lh7m<15<_7C+hRF=GULB!=#vIBvTTT6qx4mrP$-?3mM%cc~8LS zW>%iA!!&71KDC@dCzHu$%H*c)&|>>?Mr%le9!o9u3MfTh|JBSZljN4>ykqM@?(U}h zM8^8ijU-ZH5+$?&$N3fsq+*jx$;Z?CGdeUPH$uSbKa&TBj(z5@A5u(;Vlrul#3Wrw zrx;2`QZHd4Ngs-53hq^KUv3rDFgKqGSy~Gv%(hW7D2fdi+U4~UChNnDJ8Us3pL6EX zmV$f@twBo)s0ugQm6A@z!)=s1j8;oJ^GF=Vli1C(SEprMJbN_}a%<&MAJoGZQ=6^y ztPLAAp!f5T)COnJ+qs~MF&h@=#BpS&s2^>AI73!L{PR?;UYw3%UE1-Rj$qoBNC#l1 zK;5X9<*l0;c~daPGndZ5}?i_Jxn`7M1@LgOMDgT}F!if?7)|z6&0>R-*Y+6FTSdz_$X|1obTm2-fTyS!Y>(AbfIBy^A`Y7{{o0cBJn@ zqo9vJy86;?_U$rxKfYhm!&$AG(_1&si{~v=BDjGz^yLyk8mQNI*tK1`b3PJ}3fQFy z7-2u&6`|#B8;%+~0aF4HBhggjHo`Nl*+jKcH{m{&TB3MQx=xgC$7S)sXLkB`-`mmHm@3XNOdeeY z9@Q;@D~(H}m_wc?>gJJIn9MQaj77hQjpF(6k=yEAGBima?Cw5DPo}0r-`~|f{{_oE BV6OlG literal 0 HcmV?d00001 diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 2 - Memo Pad $f100 (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 2 - Memo Pad $f100 (1982) (CommaVid).a26 new file mode 100644 index 0000000000000000000000000000000000000000..14bfe5df108035cb689f43fd1f597f1ba96a518e GIT binary patch literal 4096 zcmeHJ{c9WN9e;E_%d-4-$?8>M*|=nBYH1~?YX?r(P)j-o>3XqMOU>l;hiMdVa><$v z3f^E%CE3r#J-_KC3QoH)i657)r6%d0ZA>qf_@$y*$-|#Z)5Q{BEY>L>>At`W>~~k~`|I&FZr`1+ zcZYYX-|4_k2X;EJ(}DlT4%8PALZ9qK{W-%n`gH!6Z=+p*t>W)@BXs1EzoTDM5%ese z3pi^199?|0ig5vO^v~!g!!MxU0=}g4u`6ibL>14Q0UQ5;erb9Wy$$%X&YS*;=31(V zRuHP(T1AJ^r`z~l;G19FLrb^6K zR}lKm$syzkJ-Lm~14lo{XwRuC{$d%S*RG#Mj$-dNJ_Q`zNFn)ORqW{KKpo~RN~5kP z7`}^PNwYD^FiaFND-8TnBu$9!2$KxUvP^k@xvjh+l=m-}+YZ?dm9u5rA;cgiOk6iz zCvpdzq`Np%o=G>FZwa@Sqh+C7&Xn6qthtM2qfxOilPP5~GYFMtR+?94N^QYm&Xyya zr>2(E>Fml>I?Xcdt4_xK>SZ_MyiA%AdELWt4#wr^VjMos>2f(;K4<3(PLJPt%;W6p zbb2}GahLPB&vne}@&;VqUZ&I4+1=ymaE-}@;KlFo z`2#+GukNhV1UAk3Qx7X`-c->wv=kRjgs)ruGr^oN} zb-H|=jIYz+n`X%9^YtaQ%_`b6MFueG<$N;~KP-Fs<>P&P1u4cLsE za5HX^1RRw{@Sv=XMF=e55xG1TCPbvt(g%r86MoUXj5_XqF===paObYrx|q^w^*=0BZ5#>U;2?;KEDRJEl=7Zf2I@&?tIH&Hb*5tM6! zd@}1^MEBtetc8T>WYAid%@g+3yirx{q}ZUW95TySHT8MvQUK^ zRwZvx$WT4)4uQ37u=Memj}bS|Tp3kJ>CBa}pj$7O{;(3Vw$$76&)A86V`{P#lfBV)R!1@%3BsmvmIFnlm#D=7Da zP(bVI;|s$Q6ppAgM5T}VwT8$o((QzWD_^jt;aLD0DhXy$k~ zJqWudM0Z!*Qq0^J%PgcHq$YEvVvT(*sa9XfvckSmW)JO>W!4Epga&iyP(~Lj*krz^gEmKi`XRA@Q*IOw3T|hK;7r} z$TfIEujbVH&2!Q@D?Qe`3Jdyj-b@+k*EhN4Ev0EX%tu7x;t&pVpKeLmdbgg0#tz}Q zh@@~N-aZe*ev}%T$2b9qAr3zwH{w<)0ZB@65G5(155piJSZ=75JGTWp=1nz}ucK-$ z{q;&hF^Y|8qp~+&4nMxOWms$2z5PBZ<(`&LcW>$#=|4Spr0RbY_PNU z7Bd>sfOZ1bO(Ba$KR4h2m?BBI(KQkO34gPpbfBy?Yd`%hv literal 0 HcmV?d00001 diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 3 - Target Practice $f100 (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 3 - Target Practice $f100 (1982) (CommaVid).a26 new file mode 100644 index 0000000000000000000000000000000000000000..e650f2ce4d12fd77c28e8bc5b70a76d990fa107c GIT binary patch literal 4096 zcmeHK|8EoL8Gn6tjAKH4Faly=W7UBuj#Nx)ha+ZSZJM{r}0IrN8lsd;t$oO?NmHnY~b&FJ(-IP7=?t$KWxkJ5_id7pW6VQwyX(2E=Yj($Og&~H9V&WZ1@NIneJ`$F5qhlU-wTmW~d=5 zAyipdMElSuReT!w$`|+1^;@4I^aVm6W=7HP-+LeZF+EYmXEO**Uitu;ewzdQ9s2n( zgnoIv8#x2dRq=|S&v{&(Y;Ak$1gc~(HaNqzspwFNbYTZRg zT4|R?uMN{Q9Y%DChF1z29ik2dHMGm+qRZRL&E*nX-Zous-fiAp&Xmo&5sl~|F{_&; zvfFJMM`5Hql4{T|unW`SGFvXE%gsfXzSZRlhq?Snx|mLnAXFSFHI_z-&Hf(7oYgd5 zyf~doWl9%QDHrW}-9|fJzwDrGmo<8%nRPOZm9|@3X{(2^+3hyF$JX+S&FQrra@ty3 zY;MMO#BMv{u^)2V-9Ed!oo=zW9By;A+Fgg-E>9anTN&EN(Cv(;?Qom5?Jxu2fhb<9 z$LsTW+m*0wFWVWrouTcF)6N{BTYPj&yR+5jY*j|J9(KDOR=30LW~^?;U5n7>b+&mu zo)){Oh4!>Ky;h&s+V1uEygq9!D&?dopNVQaN*(nbZ8xt9DFZnoa*KGlMT>*m()W0qD3pnExovR@9rRx z(qLi|NqTtjx=FKy8z;RIHh>H0Nrk4R9&8XlD+;La6lhlUEZTyZG+X|}n$}3?O=@w1 zXF-PL_ks+|?*kc@f1xVFVwU-6i6b$Q7ncQdb<(ImNpPxh*0M3T)}j~Tx+f9B+9dmF zSR@2F8P1b4m%@bWQI=>_g@o<>X9@rC!1$2qt-UuX(@Qw+!ePDM*b&BI;UQ#>h6f6D zQXQ!(vMwCUOzx}<4;tUxRl!j{?h*`9LXdDgr5Ou96F-+w^^o^D&BcL<=PNi)8lumQ zCtzo~l8HQd&$4}V?RY{dNR=LpjW%;I#guwe;#d#4q&RO|n!vrj=01`zKXQXajE};E z7V)W6oq%HDp0VgN@hu4zSmdG~nY|~|NZYW%_|5H-L6Qsxr6CDHl~YODoQ_lrgkMnm zBZ-V-5KXOF2SR2V9PTNhf{I8awMU;x!lpLo$$s z16C!c5;cK($`OFkg38zt!I3C2fBH(FsNqjv>GwO7bg7Rj0h6KLlG|?~I&PU7gns6a+F^9ex*eS9J>5;#hhgss4+^A+(L(Q3W0A5qF2>Q^F7Nbf64rROv|f;&Ry zy!gNm38cOw)+w-O;_9t)AK%SNce3K5l9)C!$mst{FI|Aa&$+wdBy!ep2dS1Q@74JVqQ17h>-@`gi9oanj>T2@c zJozB9MNq>h3uJR*Y5pvK)dh5qdR1IE9E^lG;zBnLGM}vS*mSpE z1BLCzF%I#;P;Bown4KuuJ&ti45Q9!+P-wtSd>kgp$Dk?lA;k(1~Y9G}B!X z0mp`G^!{X8#%Ctst1Ef@jLNs#C3ne7l`eVD%HGuvRutAH^8+qd8jb>|>=4l9>LHRH s0?*Ehv11TNhg|%CNj*w*YSMvyx8#dae-FKF{rYWmPpr@XgH`4CA02+|&;S4c literal 0 HcmV?d00001 diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 4 - Generating Your Own Display $f200 (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 4 - Generating Your Own Display $f200 (1982) (CommaVid).a26 new file mode 100644 index 0000000000000000000000000000000000000000..25a2264d5a78802f7c48d3ce8ee3427f0e9914f1 GIT binary patch literal 4096 zcmeHK|8E;-8GrA5aUAE1uVrDbRM4$-U2LYxV?#>Es>SxXvTxVKi%qe+A26B%&Qw;A zCIuwc*^b|ab% zvsLjopyez-pZD{;_dL)0JnySl&#CI(^F^+J7=8y@Y?DHU|$Y4@M*-stsId4*~GrSKG0_@f;{MdlIHqphA`@5G)>0< zy-uT7mI(u(`XU6)FbrKgRO_j&^R+`OwVtEaqqSnqdKA!rj^L|?RlIcALAaM^YqR+d z<8A)-O034$YK2-)l`-}+Oe`i;W((CqVHSYu?0VPwY_%tpV67#hYkGPmpD(UY=kpBB zyy~FcuU>c4j_ZUG5UU=RwbM>}KW+E14yV)M^f_EFIy`>INspu7G z=?yr&LE7bX4Genvoy`D42L|nf11tg`^5VDq`~jaosJR_{!O1$E zEbU}HPWBY-3ec{gr$6B7*R1*nyk586>-KtCyO;GgJq-FigMOdS<@C8|pUdO72mJP+ z-xu%)>`kwfhoS-&YVb65I&eB@3HDehI~AaU*R-Yr;t+#I*a^F!N#tQn9D}2hnvCL* zhhtJL8NmghP~sSAet%4Smh3?CTcjSz<2Jpdy2jvG@6iaJCm_BA6eD_I=aN-{T}yri znve?7a~dru322f(sY+n^F%+{A&!%6tl2+UAc}uHu*`kvdMIMFW#a$7EbmbFm8f57a0z53>Yw zShITy%}gvzS>EXV7iD=KW*8VV8qGs77?U2L%)!jWa*NVJVpX1j>EhB8^_fZY>rd8U zO3W~lDTPZ4%;bsr*yEW?Su8=`6Nu@F#i#2qLz+@gEo9NjWOCUGxoJB**S?U|3eun_ z(sMlmT4G-J^Fb)uwqgalSvaKE@5R%(U%RRPQpWyF2rSv?j>+vZWYupKa&kvT8m}e zwo%qAij5H3<@GWy>%xpXY%wUG^5)@|qI?tCAd@1>!i{#NjLAf}jdF*PwS<|6ILM{& zwevT|WkNiEBN=jQ=~D02!xmGUt^BMFAJ*rlsv)TjPEcpJ1XYaLaE=#8ksPCbu>Jl7 zSqQ=oXZ>?qJO~JrrubhLa6Df&I5o=k=fab(( zMaE%@T$c_aMqRGUH`x(Y0x*kCYkvD$1Shv=A6Y4C{dN%l)N3Hk~;z}o~P#PB8LQ3sw2%j=cY!{sR>g9b+?!@4y6i< z`3JeFQgvBp+YGynXuI&j;&--Cy-Le1w#{nTUDnB1(H*9a*WHDL(l*b*6!Ir;gsETF z!)UU*Y8F!@o>zxB_tcOUFpU1}YbuKOzxeZ96=_S?aoeVZBTF=YCXP8z2EGxvDX2e} zfMCtNk#kn$2f{~})w`(T$x)oTx`RGC41$h(bm^tt+*=j$K6e0picD(RoZVWxC|0Xs(3i_bQcu0U!>;Vgoih=rXubSn{UO7=<_IZ3l@~l zl7b5=Pi}J&G|SuWlJ}YkqP!j`w~fV0rQAh+>EV@uY4-=ZHki44O9d5PSdvtjJ97aG z?2f1->Q;S3eP*Y3_njS$ji}-T!xYd};8EQYxZb!#N>fPlMBO|z2~$&yIAPJv;lo5B aeC&=ooeCxBL;Lm}q7&)y(06yW-+uwT-ATy+ literal 0 HcmV?d00001 diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 5 - Life $f15e (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 5 - Life $f15e (1982) (CommaVid).a26 new file mode 100644 index 0000000000000000000000000000000000000000..3cf4661191e0c0de8f783ebe1abdd34ffa941789 GIT binary patch literal 4096 zcmeHK|8EoL8Gn6tjN^p(U}Oyvo34y6aaJLB?Ih@mitP)s-6OcMOC9>dJXKkpij_^O z#3rrOHhE2Yi4(`hD6B&>r@YK(mCT_8a9Acb738xz?aJ1PN}HN7$h?S}V+r39C%5Nf zq5Tgl{_=d@&-32Q z;7L-zgJcp9X$$kc;{w`&6h&8xU1AO{jE<9h7gE4C;ca(F-mStUK1=eOFV&sZv{9)} zt{2tzOZXBOzAI6(iA*YIu1?aTWPou&Id!!_pOlQUnG~e0bZVwR4@oBNDalIXnMuOS zc8CNIL9&C)#=Er--_m`d`+WB~iLy0Idc{av$T_8fub>rO0BlP&J2+uz>m2% z?|!vU%9HG$RNzSko>bufs{-{UgwQ7&P)|{}fx9QYXhpzjs*OTgDP9>0Q~9;)F9GhpN2&`(V7qYnVz(D>@Vqj5_O z(JVsM+w<^y_~{CM3;5EP_t8J@e2&nU2wlwO(QiLIhklW~JhEREIwrO^(+#2lT z+KPsz(a{^3Om22Glku>gw;inO?aMCKaoJ!-hMR7Vv$IZn2W$6n4yV)M^f}sJak%}C zJ#I%wyTi*l_BkEyr#ImAcCqcw_Pw3%4yR|2*W>HtSUblCIJS%Pb?)u7ckbl? zd|<_I_xS@pf0t&q^JORJbaJedb33_xYVlxO0@+@?(`jE#JvYM1H_?Di!oO~VD zz>*wf;euU}bh05-&$vRcTGX|4$8an`%^bZlAREZhEAgO9OP86fhOCx)oAiQ>>M^EA z%0aOnTHAvQ0(8qZD#nu|Ap4m&7q1S|L6i(2y;s#*M$*O#vJETGzor2SvSv(V_=*6*K zO3Z;lAht!GZj@3GZ|aAM;zjQlBep+pN$gJLOQIoHcN8Z{Oe=FFu30K5=d`Ud3x+M> zEs?g8{4fX!WWFocY4CjE+MN@h%oUaY6y

F=K24BJIz3$HCey^7OywtMA}OVZ1_ zoGZlath%y&O8s~YC%~ROA7Xx04Z&n{xrHQXDx-GCTqE6@!w`JAcU18AKmX&gGH8pJ zDce1ff|W3TpjYSI6ZlTxx}g3-L_%Boy|l9=KN7B;ROg`L@go%T;ga|&HVOLJQ&X=m zr#~ps4`b^^J^V+4Yz{8W94E)EOuTCzHuMe2OdFYZm$)0t^6Jy!SVW*s_2Mx1=`z9A zxq1T>wigczh=e1<+b3YygVMbd7$*TS_=ydPjkuX4AxJU|qD&&%Fbo2m6?>bxD!Q(vr(RCVMxildls8EAFfwHf@>BU$WW_jBjeZQ6<6oZO)ZSQWO2P$5)0< zyFcLC@Z|4LWmSA^N>p+Bz%gB5`H*@@U8o*XpI_R(e11t|hg34?@nqpDaH}p6U0%6F uiX))eT5ahb!ifIn literal 0 HcmV?d00001 diff --git a/test/roms/bankswitching/CV/Video Life (CommaVid) [h1].a26 b/test/roms/bankswitching/CV/Video Life (CommaVid) [h1].a26 deleted file mode 100644 index a38a8e7b127c389d7765e65848e46d29d1e94bb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 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