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