1 //============================================================================ 2 // 3 // SSSS tt lll lll 4 // SS SS tt ll ll 5 // SS tttttt eeee ll ll aaaa 6 // SSSS tt ee ee ll ll aa 7 // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" 8 // SS SS tt ee ll ll aa aa 9 // SSSS ttt eeeee llll llll aaaaa 10 // 11 // Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony 12 // and the Stella Team 13 // 14 // See the file "License.txt" for information on usage and redistribution of 15 // this file, and for a DISCLAIMER OF ALL WARRANTIES. 16 //============================================================================ 17 18 #ifndef CARTRIDGE_HXX 19 #define CARTRIDGE_HXX 20 21 class Cartridge; 22 class Properties; 23 class FilesystemNode; 24 class CartDebugWidget; 25 class CartRamWidget; 26 class GuiObject; 27 class Settings; 28 29 #include <functional> 30 31 #include "bspf.hxx" 32 #include "Device.hxx" 33 #ifdef DEBUGGER_SUPPORT 34 namespace GUI { 35 class Font; 36 } 37 #endif 38 39 /** 40 A cartridge is a device which contains the machine code for a 41 game and handles any bankswitching performed by the cartridge. 42 A 'bank' is defined as a 4K block that is visible in the 43 0x1000-0x2000 area (or its mirrors). 44 45 @author Bradford W. Mott 46 */ 47 class Cartridge : public Device 48 { 49 public: 50 using StartBankFromPropsFunc = std::function<int()>; 51 52 /** 53 Callback type for general cart messages 54 */ 55 using messageCallback = std::function<void(const string&)>; 56 57 // Maximum size of a ROM cart that Stella can support maxSize()58 static constexpr size_t maxSize() { return 512_KB; } 59 60 public: 61 /** 62 Create a new cartridge 63 64 @param settings A reference to the various settings (read-only) 65 @param md5 The md5sum of the cart image 66 */ 67 Cartridge(const Settings& settings, const string& md5); 68 ~Cartridge() override = default; 69 70 /** 71 Set/query some information about this cartridge. 72 */ 73 void setAbout(const string& about, const string& type, const string& id); about() const74 const string& about() const { return myAbout; } detectedType() const75 const string& detectedType() const { return myDetectedType; } multiCartID() const76 const string& multiCartID() const { return myMultiCartID; } 77 78 /** 79 Save the internal (patched) ROM image. 80 81 @param out The output file to save the image 82 */ 83 bool saveROM(const FilesystemNode& out) const; 84 85 /** 86 Lock/unlock bankswitching and other hotspot capabilities. The debugger 87 will lock the hotspots before querying the cart state, otherwise reading 88 values could inadvertantly cause e.g. a bankswitch to occur. 89 */ lockHotspots()90 void lockHotspots() { myHotspotsLocked = true; } unlockHotspots()91 void unlockHotspots() { myHotspotsLocked = false; } hotspotsLocked() const92 bool hotspotsLocked() const { return myHotspotsLocked; } 93 94 /** 95 Get the default startup bank for a cart. This is the bank where 96 the system will look at address 0xFFFC to determine where to 97 start running code. 98 99 @return The startup bank 100 */ startBank() const101 uInt16 startBank() const { return myStartBank; } 102 103 /** 104 Set the function to use when we want to query the 'Cartridge.StartBank' 105 ROM property. 106 */ setStartBankFromPropsFunc(const StartBankFromPropsFunc & func)107 void setStartBankFromPropsFunc(const StartBankFromPropsFunc& func) { 108 myStartBankFromPropsFunc = func; 109 } 110 111 /** 112 Answer whether the bank has changed since the last time this 113 method was called. Each cart class is able to override this 114 method to deal with its specific functionality. In those cases, 115 the derived class is still responsible for calling this base 116 function. 117 118 @return Whether the bank was changed 119 */ 120 virtual bool bankChanged(); 121 122 /** 123 Query the internal RAM size of the cart. 124 125 @return The internal RAM size 126 */ internalRamSize() const127 virtual uInt32 internalRamSize() const { return 0; } 128 129 /** 130 Read a byte from cart internal RAM. 131 132 @return The value of the interal RAM byte 133 */ internalRamGetValue(uInt16 addr) const134 virtual uInt8 internalRamGetValue(uInt16 addr) const { return 0; } 135 136 /** 137 Answer whether this is a PlusROM cart. Note that until the 138 initialize method has been called, this will always return false. 139 140 @return Whether this is actually a PlusROM cart 141 */ isPlusROM() const142 virtual bool isPlusROM() const { return false; } 143 144 /** 145 Set the callback for displaying messages 146 */ setMessageCallback(const messageCallback & callback)147 virtual void setMessageCallback(const messageCallback& callback) 148 { 149 myMsgCallback = callback; 150 } 151 152 #ifdef DEBUGGER_SUPPORT 153 /** 154 To be called at the start of each instruction. 155 Clears information about all accesses to cart RAM. 156 */ clearAllRAMAccesses()157 void clearAllRAMAccesses() { 158 myRamReadAccesses.clear(); 159 myRamWriteAccess = 0; 160 } 161 162 /** 163 To be called at the end of each instruction. 164 Answers whether an access in the last instruction cycle generated 165 an illegal read RAM access. 166 167 @return Address of illegal access if one occurred, else 0 168 */ getIllegalRAMReadAccess() const169 uInt16 getIllegalRAMReadAccess() const { 170 return myRamReadAccesses.size() > 0 ? myRamReadAccesses[0] : 0; 171 } 172 173 /** 174 To be called at the end of each instruction. 175 Answers whether an access in the last instruction cycle generated 176 an illegal RAM write access. 177 178 @return Address of illegal access if one occurred, else 0 179 */ getIllegalRAMWriteAccess() const180 uInt16 getIllegalRAMWriteAccess() const { return myRamWriteAccess; } 181 182 /** 183 Query the access counters 184 185 @return The access counters as comma separated string 186 */ 187 string getAccessCounters() const override; 188 189 /** 190 Determine the bank's origin 191 192 @param bank The bank to query 193 @return The origin of the bank 194 */ 195 uInt16 bankOrigin(uInt16 bank) const; 196 #endif 197 198 public: 199 ////////////////////////////////////////////////////////////////////// 200 // The following methods are cart-specific and will usually be 201 // implemented in derived classes. Carts which don't support 202 // bankswitching (for any reason) do not have to provide an 203 // implementation for bankswitch-related methods. 204 ////////////////////////////////////////////////////////////////////// 205 /** 206 Set the specified bank. This is used only when the bankswitching 207 scheme defines banks in a standard format (ie, 0 for first bank, 208 1 for second, etc). Carts which will handle their own bankswitching 209 completely or non-bankswitched carts can ignore this method. 210 211 @param bank The bank that should be installed in the system 212 @param segment The segment the bank should be using 213 214 @return true, if bank has changed 215 */ bank(uInt16 bank,uInt16 segment=0)216 virtual bool bank(uInt16 bank, uInt16 segment = 0) { return false; } 217 218 /** 219 Get the current bank for the provided address. Carts which have only 220 one bank (either real or virtual) always report that bank as zero. 221 222 @param address Query the bank used for this specific address 223 Derived classes are free to ignore this; it only 224 makes sense in some situations. 225 */ getBank(uInt16 address=0) const226 virtual uInt16 getBank(uInt16 address = 0) const { return 0; } 227 228 /** 229 Query the number of ROM 'banks' supported by the cartridge. Note that 230 this information is cart-specific, where each cart basically defines 231 what a 'bank' is. 232 233 For the normal Atari-manufactured carts, a standard bank is a 4K 234 block that is directly accessible in the 4K address space. In other 235 cases where ROMs have 2K blocks in some preset area, the bankCount 236 is the number of such blocks. Finally, in some esoteric schemes, 237 the number of ways that the addressing can change (multiple ROM and 238 RAM segments at multiple access points) is so complicated that the 239 cart will report having only one 'virtual' bank. 240 */ romBankCount() const241 virtual uInt16 romBankCount() const { return 1; } 242 243 /** 244 Query the number of RAM 'banks' supported by the cartridge. Note that 245 this information is cart-specific, where each cart basically defines 246 what a 'bank' is. 247 */ ramBankCount() const248 virtual uInt16 ramBankCount() const { return 0; } 249 250 /** 251 Get the size of a bank. 252 253 @param bank The bank to get the size for 254 @return The bank's size 255 */ 256 virtual uInt16 bankSize(uInt16 bank = 0) const; 257 258 /** 259 Patch the cartridge ROM. 260 261 @param address The ROM address to patch 262 @param value The value to place into the address 263 @return Success or failure of the patch operation 264 */ 265 virtual bool patch(uInt16 address, uInt8 value) = 0; 266 267 /** 268 Access the internal ROM image for this cartridge. 269 270 @param size Set to the size of the internal ROM image data 271 @return A reference to the internal ROM image data 272 */ 273 virtual const ByteBuffer& getImage(size_t& size) const = 0; 274 275 /** 276 Get a descriptor for the cart name. 277 278 @return The name of the cart 279 */ 280 virtual string name() const = 0; 281 282 /** 283 Informs the cartridge about the name of the nvram file it will 284 use; not all carts support this. 285 286 @param nvramdir The full path of the nvram directory 287 @param romfile The name of the cart from ROM properties 288 */ setNVRamFile(const string & nvramdir,const string & romfile)289 virtual void setNVRamFile(const string& nvramdir, const string& romfile) { } 290 291 /** 292 Thumbulator only supports 16-bit ARM code. Some Harmony/Melody drivers, 293 such as BUS and CDF, feature 32-bit ARM code subroutines. This is used 294 to pass values back to the cartridge class to emulate those subroutines. 295 */ thumbCallback(uInt8 function,uInt32 value1,uInt32 value2)296 virtual uInt32 thumbCallback(uInt8 function, uInt32 value1, uInt32 value2) { return 0; } 297 298 #ifdef DEBUGGER_SUPPORT 299 /** 300 Get optional debugger widget responsible for displaying info about the cart. 301 This can be used when the debugWidget runs out of space. 302 */ infoWidget(GuiObject * boss,const GUI::Font & lfont,const GUI::Font & nfont,int x,int y,int w,int h)303 virtual CartDebugWidget* infoWidget(GuiObject* boss, const GUI::Font& lfont, 304 const GUI::Font& nfont, int x, int y, int w, int h) 305 { 306 return nullptr; 307 } 308 309 /** 310 Get debugger widget responsible for accessing the inner workings 311 of the cart. This will need to be overridden and implemented by 312 each specific cart type, since the bankswitching/inner workings 313 of each cart type can be very different from each other. 314 */ debugWidget(GuiObject * boss,const GUI::Font & lfont,const GUI::Font & nfont,int x,int y,int w,int h)315 virtual CartDebugWidget* debugWidget(GuiObject* boss, const GUI::Font& lfont, 316 const GUI::Font& nfont, int x, int y, int w, int h) 317 { 318 return nullptr; 319 } 320 #endif 321 322 protected: 323 /** 324 Get a random value to use when a read from the write port happens. 325 Sometimes a RWP means that RAM should be overwritten, sometimes not. 326 327 Internally, this method also keeps track of illegal accesses. 328 329 @param dest The location to place the value, when an overwrite should happen 330 @param address The address of the illegal read 331 @return The value read, whether it is overwritten or not 332 */ 333 uInt8 peekRAM(uInt8& dest, uInt16 address); 334 335 /** 336 Use the given value when writing to RAM. 337 338 Internally, this method also keeps track of legal accesses, and removes 339 them from the illegal list. 340 341 @param dest The final location (including address) to place the value 342 @param address The address of the legal write 343 @param value The value to write to the given address 344 */ 345 void pokeRAM(uInt8& dest, uInt16 address, uInt8 value); 346 347 /** 348 Create an array that holds code-access information for every byte 349 of the ROM (indicated by 'size'). Note that this is only used by 350 the debugger, and is unavailable otherwise. 351 352 @param size The size of the code-access array to create 353 */ 354 void createRomAccessArrays(size_t size); 355 356 /** 357 Fill the given RAM array with (possibly random) data. 358 359 @param arr Pointer to the RAM array 360 @param size The size of the RAM array 361 @param val If provided, the value to store in the RAM array 362 */ 363 void initializeRAM(uInt8* arr, size_t size, uInt8 val = 0) const; 364 365 /** 366 Set the start bank to be used when the cart is reset. This method 367 will take both randomization and properties settings into account. 368 See the actual method for more information on the logic used. 369 370 NOTE: If this method is used, it *must* be called from the cart reset() 371 method, *not* from the c'tor. 372 373 @param defaultBank The default bank to use during reset, if 374 randomization or properties aren't being used 375 376 @return The bank number that was determined 377 */ 378 uInt16 initializeStartBank(uInt16 defaultBank); 379 380 /** 381 Checks if initial RAM randomization is enabled. 382 383 @return Whether the initial RAM should be randomized 384 */ 385 bool randomInitialRAM() const; 386 387 /** 388 Checks if startup bank randomization is enabled. 389 390 @return Whether the startup bank(s) should be randomized 391 */ 392 virtual bool randomStartBank() const; 393 394 protected: 395 // Settings class for the application 396 const Settings& mySettings; 397 398 // Indicates if the bank has changed somehow (a bankswitch has occurred) 399 bool myBankChanged{true}; 400 401 // The array containing information about every byte of ROM indicating 402 // whether it is used as code, data, graphics etc. 403 std::unique_ptr<Device::AccessFlags[]> myRomAccessBase; 404 405 // The array containing information about every byte of ROM indicating 406 // how often it is accessed. 407 std::unique_ptr<Device::AccessCounter[]> myRomAccessCounter; 408 409 410 // Contains address of illegal RAM write access or 0 411 uInt16 myRamWriteAccess{0}; 412 413 // Total size of ROM access area (might include RAM too) 414 uInt32 myAccessSize; 415 416 // Callback to output messages 417 messageCallback myMsgCallback{nullptr}; 418 419 private: 420 // The startup bank to use (where to look for the reset vector address) 421 uInt16 myStartBank{0}; 422 423 // If myHotspotsLocked is true, ignore attempts at bankswitching. This is used 424 // by the debugger, when disassembling/dumping ROM. 425 bool myHotspotsLocked{false}; 426 427 // Semi-random values to use when a read from write port occurs 428 std::array<uInt8, 256> myRWPRandomValues; 429 430 // Contains various info about this cartridge 431 // This needs to be stored separately from child classes, since 432 // sometimes the information in both do not match 433 // (ie, detected type could be '2in1' while name of cart is '4K') 434 string myAbout, myDetectedType, myMultiCartID; 435 436 // Used when we want the 'Cartridge.StartBank' ROM property 437 StartBankFromPropsFunc myStartBankFromPropsFunc; 438 439 // Used to answer whether an access in the last instruction cycle 440 // generated an illegal read RAM access. Contains address of illegal 441 // access. 442 ShortArray myRamReadAccesses; 443 444 // Following constructors and assignment operators not supported 445 Cartridge() = delete; 446 Cartridge(const Cartridge&) = delete; 447 Cartridge(Cartridge&&) = delete; 448 Cartridge& operator=(const Cartridge&) = delete; 449 Cartridge& operator=(Cartridge&&) = delete; 450 }; 451 452 #endif 453