// // Copyright (c) 2004 K. Wilkins // // This software is provided 'as-is', without any express or implied warranty. // In no event will the authors be held liable for any damages arising from // the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source distribution. // ////////////////////////////////////////////////////////////////////////////// // Handy - An Atari Lynx Emulator // // Copyright (c) 1996,1997 // // K. Wilkins // ////////////////////////////////////////////////////////////////////////////// // Lynx Cartridge Class // ////////////////////////////////////////////////////////////////////////////// // // // This class emulates the Lynx cartridge interface, given a filename it // // will contstruct a cartridge object via the constructor. // // // // K. Wilkins // // August 1997 // // // ////////////////////////////////////////////////////////////////////////////// // Revision History: // // ----------------- // // // // 01Aug1997 KW Document header added & class documented. // // // ////////////////////////////////////////////////////////////////////////////// #define CART_CPP //#include //#define TRACE_CART #include #include #include #include "System.h" #include "Cart.h" #include "./zlib-113/zlib.h" CCart::CCart(UBYTE *gamedata,ULONG gamesize) { TRACE_CART1("CCart() called with %s",gamefile); LYNX_HEADER header; mWriteEnableBank0=FALSE; mWriteEnableBank1=FALSE; mCartRAM=FALSE; mHeaderLess=FALSE; mCRC32=0; mCRC32=crc32(mCRC32,gamedata,gamesize); // Open up the file if(gamesize) { // Checkout the header bytes memcpy(&header,gamedata,sizeof(LYNX_HEADER)); #ifdef MSB_FIRST header.page_size_bank0 = ((header.page_size_bank0>>8) | (header.page_size_bank0<<8)); header.page_size_bank1 = ((header.page_size_bank1>>8) | (header.page_size_bank1<<8)); header.version = ((header.version>>8) | (header.version<<8)); #endif // Sanity checks on the header if(header.magic[0]!='L' || header.magic[1]!='Y' || header.magic[2]!='N' || header.magic[3]!='X' || header.version!=1) { CLynxException lynxerr; lynxerr.Message() << "Handy Error: File format invalid (Magic No)"; lynxerr.Description() << "The image you selected was not a recognised game cartridge format." << endl << "(see the Handy User Guide for more information)."; throw(lynxerr); } // Setup name & manufacturer strncpy(mName,(char*)&header.cartname,32);; strncpy(mManufacturer,(char*)&header.manufname,16); // Setup rotation mRotation=header.rotation; if(mRotation!=CART_NO_ROTATE && mRotation!=CART_ROTATE_LEFT && mRotation!=CART_ROTATE_RIGHT) mRotation=CART_NO_ROTATE; } else { header.page_size_bank0=0x000; header.page_size_bank1=0x000; // Setup name & manufacturer strcpy(mName,""); strcpy(mManufacturer,""); // Setup rotation mRotation=CART_NO_ROTATE; } // Set the filetypes CTYPE banktype0,banktype1; switch(header.page_size_bank0) { case 0x000: banktype0=UNUSED; mMaskBank0=0; mShiftCount0=0; mCountMask0=0; break; case 0x100: banktype0=C64K; mMaskBank0=0x00ffff; mShiftCount0=8; mCountMask0=0x0ff; break; case 0x200: banktype0=C128K; mMaskBank0=0x01ffff; mShiftCount0=9; mCountMask0=0x1ff; break; case 0x400: banktype0=C256K; mMaskBank0=0x03ffff; mShiftCount0=10; mCountMask0=0x3ff; break; case 0x800: banktype0=C512K; mMaskBank0=0x07ffff; mShiftCount0=11; mCountMask0=0x7ff; break; default: CLynxException lynxerr; lynxerr.Message() << "Handy Error: File format invalid (Bank0)"; lynxerr.Description() << "The image you selected was not a recognised game cartridge format." << endl << "(see the Handy User Guide for more information)."; throw(lynxerr); break; } TRACE_CART1("CCart() - Bank0 = $%06x",mMaskBank0); switch(header.page_size_bank1) { case 0x000: banktype1=UNUSED; mMaskBank1=0; mShiftCount1=0; mCountMask1=0; break; case 0x100: banktype1=C64K; mMaskBank1=0x00ffff; mShiftCount1=8; mCountMask1=0x0ff; break; case 0x200: banktype1=C128K; mMaskBank1=0x01ffff; mShiftCount1=9; mCountMask1=0x1ff; break; case 0x400: banktype1=C256K; mMaskBank1=0x03ffff; mShiftCount1=10; mCountMask1=0x3ff; break; case 0x800: banktype1=C512K; mMaskBank1=0x07ffff; mShiftCount1=11; mCountMask1=0x7ff; break; default: CLynxException lynxerr; lynxerr.Message() << "Handy Error: File format invalid (Bank1)"; lynxerr.Description() << "The image you selected was not a recognised game cartridge format." << endl << "(see the Handy User Guide for more information)."; throw(lynxerr); break; } TRACE_CART1("CCart() - Bank1 = $%06x",mMaskBank1); // Make some space for the new carts mCartBank0 = (UBYTE*) new UBYTE[mMaskBank0+1]; mCartBank1 = (UBYTE*) new UBYTE[mMaskBank1+1]; // Set default bank mBank=bank0; // Initialiase int cartsize = __max(0, int(gamesize - sizeof(LYNX_HEADER))); int bank0size = __min(cartsize, (int)(mMaskBank0+1)); int bank1size = __min(cartsize, (int)(mMaskBank1+1)); memcpy( mCartBank0, gamedata+(sizeof(LYNX_HEADER)), bank0size); memset( mCartBank0 + bank0size, DEFAULT_CART_CONTENTS, mMaskBank0+1 - bank0size); memcpy( mCartBank1, gamedata+(sizeof(LYNX_HEADER)), bank1size); memset( mCartBank1 + bank0size, DEFAULT_CART_CONTENTS, mMaskBank1+1 - bank1size); // Copy the cart banks from the image if(gamesize) { // As this is a cartridge boot unset the boot address gCPUBootAddress=0; // // Check if this is a headerless cart // mHeaderLess=TRUE; for(int loop=0;loop<32;loop++) { if(mCartBank0[loop&mMaskBank0]!=0x00) mHeaderLess=FALSE; } TRACE_CART1("CCart() - mHeaderLess=%d",mHeaderLess); } // Dont allow an empty Bank1 - Use it for shadow SRAM/EEPROM if(banktype1==UNUSED) { // Delete the single byte allocated earlier delete[] mCartBank1; // Allocate some new memory for us TRACE_CART0("CCart() - Bank1 being converted to 64K SRAM"); banktype1=C64K; mMaskBank1=0x00ffff; mShiftCount1=8; mCountMask1=0x0ff; mCartBank1 = (UBYTE*) new UBYTE[mMaskBank1+1]; memset(mCartBank1, DEFAULT_RAM_CONTENTS, mMaskBank1+1); mWriteEnableBank1=TRUE; mCartRAM=TRUE; } } CCart::~CCart() { TRACE_CART0("~CCart()"); delete[] mCartBank0; delete[] mCartBank1; } void CCart::Reset(void) { TRACE_CART0("Reset()"); mCounter=0; mShifter=0; mAddrData=0; mStrobe=0; } bool CCart::ContextSave(FILE *fp) { TRACE_CART0("ContextSave()"); #ifdef GZIP_STATE #define fwrite(B,L,N,F) gzwrite(F,B,(L)*(N)) #define fprintf(F,S) gzprintf(F,S) #endif if(!fprintf(fp,"CCart::ContextSave")) return 0; if(!fwrite(&mCounter,sizeof(ULONG),1,fp)) return 0; if(!fwrite(&mShifter,sizeof(ULONG),1,fp)) return 0; if(!fwrite(&mAddrData,sizeof(ULONG),1,fp)) return 0; if(!fwrite(&mStrobe,sizeof(ULONG),1,fp)) return 0; if(!fwrite(&mShiftCount0,sizeof(ULONG),1,fp)) return 0; if(!fwrite(&mCountMask0,sizeof(ULONG),1,fp)) return 0; if(!fwrite(&mShiftCount1,sizeof(ULONG),1,fp)) return 0; if(!fwrite(&mCountMask1,sizeof(ULONG),1,fp)) return 0; if(!fwrite(&mBank,sizeof(EMMODE),1,fp)) return 0; if(!fwrite(&mWriteEnableBank0,sizeof(ULONG),1,fp)) return 0; if(!fwrite(&mWriteEnableBank1,sizeof(ULONG),1,fp)) return 0; if(!fwrite(&mCartRAM,sizeof(ULONG),1,fp)) return 0; if(mCartRAM) { if(!fwrite(&mMaskBank1,sizeof(ULONG),1,fp)) return 0; if(!fwrite(mCartBank1,sizeof(UBYTE),mMaskBank1+1,fp)) return 0; } #ifdef GZIP_STATE #undef fwrite #undef fprintf #endif return 1; } bool CCart::ContextLoad(LSS_FILE *fp) { TRACE_CART0("ContextLoad()"); char teststr[100]="XXXXXXXXXXXXXXXXXX"; if(!lss_read(teststr,sizeof(char),18,fp)) return 0; if(strcmp(teststr,"CCart::ContextSave")!=0) return 0; if(!lss_read(&mCounter,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mShifter,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mAddrData,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mStrobe,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mShiftCount0,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mCountMask0,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mShiftCount1,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mCountMask1,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mBank,sizeof(EMMODE),1,fp)) return 0; if(!lss_read(&mWriteEnableBank0,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mWriteEnableBank1,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mCartRAM,sizeof(ULONG),1,fp)) return 0; if(mCartRAM) { if(!lss_read(&mMaskBank1,sizeof(ULONG),1,fp)) return 0; delete[] mCartBank1; mCartBank1 = new UBYTE[mMaskBank1+1]; if(!lss_read(mCartBank1,sizeof(UBYTE),mMaskBank1+1,fp)) return 0; } return 1; } bool CCart::ContextLoadLegacy(LSS_FILE *fp) { TRACE_CART0("ContextLoadLegacy()"); strcpy(mName,"<** IMAGE **>"); strcpy(mManufacturer,"<** RESTORED **>"); char teststr[100]="XXXXXXXXXXXXXXXXXX"; if(!lss_read(teststr,sizeof(char),18,fp)) return 0; if(strcmp(teststr,"CCart::ContextSave")!=0) return 0; if(!lss_read(&mRotation,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mHeaderLess,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mCounter,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mShifter,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mAddrData,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mStrobe,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mShiftCount0,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mCountMask0,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mShiftCount1,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mCountMask1,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mBank,sizeof(EMMODE),1,fp)) return 0; if(!lss_read(&mMaskBank0,sizeof(ULONG),1,fp)) return 0; if(!lss_read(&mMaskBank1,sizeof(ULONG),1,fp)) return 0; delete[] mCartBank0; delete[] mCartBank1; mCartBank0 = new UBYTE[mMaskBank0+1]; mCartBank1 = new UBYTE[mMaskBank1+1]; if(!lss_read(mCartBank0,sizeof(UBYTE),mMaskBank0+1,fp)) return 0; if(!lss_read(mCartBank1,sizeof(UBYTE),mMaskBank1+1,fp)) return 0; return 1; } inline void CCart::Poke(ULONG addr, UBYTE data) { if(mBank==bank0) { if(mWriteEnableBank0) mCartBank0[addr&mMaskBank0]=data; } else { if(mWriteEnableBank1) mCartBank1[addr&mMaskBank1]=data; } } inline UBYTE CCart::Peek(ULONG addr) { if(mBank==bank0) { return(mCartBank0[addr&mMaskBank0]); } else { return(mCartBank1[addr&mMaskBank1]); } } void CCart::CartAddressStrobe(bool strobe) { static int last_strobe=0; mStrobe=strobe; if(mStrobe) mCounter=0; // // Either of the two below seem to work OK. // // if(!strobe && last_strobe) // if(mStrobe && !last_strobe) { // Clock a bit into the shifter mShifter=mShifter<<1; mShifter+=mAddrData?1:0; mShifter&=0xff; } last_strobe=mStrobe; TRACE_CART2("CartAddressStrobe(strobe=%d) mShifter=$%06x",strobe,mShifter); } void CCart::CartAddressData(bool data) { TRACE_CART1("CartAddressData($%02x)",data); mAddrData=data; } void CCart::Poke0(UBYTE data) { if(mWriteEnableBank0) { ULONG address=(mShifter<