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-2019 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
21class Cartridge;
22class Properties;
23class CartDebugWidget;
24class CartRamWidget;
25class GuiObject;
26
27#include "bspf.hxx"
28#include "Device.hxx"
29#include "Settings.hxx"
30#ifdef DEBUGGER_SUPPORT
31 #include "Font.hxx"
32#endif
33
34/**
35 A cartridge is a device which contains the machine code for a
36 game and handles any bankswitching performed by the cartridge.
37 A 'bank' is defined as a 4K block that is visible in the
38 0x1000-0x2000 area (or its mirrors).
39
40 @author Bradford W. Mott
41*/
42class Cartridge : public Device
43{
44 public:
45 using StartBankFromPropsFunc = std::function<int()>;
46
47 public:
48 /**
49 Create a new cartridge
50
51 @param settings A reference to the various settings (read-only)
52 @param md5 The md5sum of the cart image
53 */
54 Cartridge(const Settings& settings, const string& md5);
55 virtual ~Cartridge() = default;
56
57 /**
58 Set/query some information about this cartridge.
59 */
60 void setAbout(const string& about, const string& type, const string& id);
61 const string& about() const { return myAbout; }
62 const string& detectedType() const { return myDetectedType; }
63 const string& multiCartID() const { return myMultiCartID; }
64
65 /**
66 Save the internal (patched) ROM image.
67
68 @param out The output file stream to save the image
69 */
70 bool saveROM(ofstream& out) const;
71
72 /**
73 Lock/unlock bankswitching capability. The debugger will lock
74 the banks before querying the cart state, otherwise reading values
75 could inadvertantly cause a bankswitch to occur.
76 */
77 void lockBank() { myBankLocked = true; }
78 void unlockBank() { myBankLocked = false; }
79 bool bankLocked() const { return myBankLocked; }
80
81 /**
82 Get the default startup bank for a cart. This is the bank where
83 the system will look at address 0xFFFC to determine where to
84 start running code.
85
86 @return The startup bank
87 */
88 uInt16 startBank() const { return myStartBank; }
89
90 /**
91 Set the function to use when we want to query the 'Cartridge.StartBank'
92 ROM property.
93 */
94 void setStartBankFromPropsFunc(StartBankFromPropsFunc func) {
95 myStartBankFromPropsFunc = func;
96 }
97
98 /**
99 Answer whether the bank has changed since the last time this
100 method was called. Each cart class is able to override this
101 method to deal with its specific functionality. In those cases,
102 the derived class is still responsible for calling this base
103 function.
104
105 @return Whether the bank was changed
106 */
107 virtual bool bankChanged();
108
109 #ifdef DEBUGGER_SUPPORT
110 /**
111 To be called at the start of each instruction.
112 Clears information about all accesses to cart RAM.
113 */
114 void clearAllRAMAccesses() {
115 myRAMAccesses.clear();
116 myRamWriteAccess = 0;
117 }
118
119 /**
120 To be called at the end of each instruction.
121 Answers whether an access in the last instruction cycle generated
122 an illegal read RAM access.
123
124 @return Address of illegal access if one occurred, else 0
125 */
126 uInt16 getIllegalRAMReadAccess() const {
127 return myRAMAccesses.size() > 0 ? myRAMAccesses[0] : 0;
128 }
129
130 /**
131 To be called at the end of each instruction.
132 Answers whether an access in the last instruction cycle generated
133 an illegal RAM write access.
134
135 @return Address of illegal access if one occurred, else 0
136 */
137 uInt16 getIllegalRAMWriteAccess() const { return myRamWriteAccess; }
138 #endif
139
140 public:
141 //////////////////////////////////////////////////////////////////////
142 // The following methods are cart-specific and will usually be
143 // implemented in derived classes. Carts which don't support
144 // bankswitching (for any reason) do not have to provide an
145 // implementation for bankswitch-related methods.
146 //////////////////////////////////////////////////////////////////////
147 /**
148 Set the specified bank. This is used only when the bankswitching
149 scheme defines banks in a standard format (ie, 0 for first bank,
150 1 for second, etc). Carts which will handle their own bankswitching
151 completely or non-bankswitched carts can ignore this method.
152 */
153 virtual bool bank(uInt16) { return false; }
154
155 /**
156 Get the current bank for the provided address. Carts which have only
157 one bank (either real or virtual) always report that bank as zero.
158
159 @param address Query the bank used for this specific address
160 Derived classes are free to ignore this; it only
161 makes sense in some situations.
162 */
163 virtual uInt16 getBank(uInt16 address = 0) const { return 0; }
164
165 /**
166 Query the number of 'banks' supported by the cartridge. Note that
167 this information is cart-specific, where each cart basically defines
168 what a 'bank' is.
169
170 For the normal Atari-manufactured carts, a standard bank is a 4K
171 block that is directly accessible in the 4K address space. In other
172 cases where ROMs have 2K blocks in some preset area, the bankCount
173 is the number of such blocks. Finally, in some esoteric schemes,
174 the number of ways that the addressing can change (multiple ROM and
175 RAM slices at multiple access points) is so complicated that the
176 cart will report having only one 'virtual' bank.
177 */
178 virtual uInt16 bankCount() const { return 1; }
179
180 /**
181 Patch the cartridge ROM.
182
183 @param address The ROM address to patch
184 @param value The value to place into the address
185 @return Success or failure of the patch operation
186 */
187 virtual bool patch(uInt16 address, uInt8 value) = 0;
188
189 /**
190 Access the internal ROM image for this cartridge.
191
192 @param size Set to the size of the internal ROM image data
193 @return A pointer to the internal ROM image data
194 */
195 virtual const uInt8* getImage(size_t& size) const = 0;
196
197 /**
198 Get a descriptor for the cart name.
199
200 @return The name of the cart
201 */
202 virtual string name() const = 0;
203
204 /**
205 Informs the cartridge about the name of the nvram file it will
206 use; not all carts support this.
207
208 @param nvramdir The full path of the nvram directory
209 @param romfile The name of the cart from ROM properties
210 */
211 virtual void setNVRamFile(const string& nvramdir, const string& romfile) { }
212
213 /**
214 Thumbulator only supports 16-bit ARM code. Some Harmony/Melody drivers,
215 such as BUS and CDF, feature 32-bit ARM code subroutines. This is used
216 to pass values back to the cartridge class to emulate those subroutines.
217 */
218 virtual uInt32 thumbCallback(uInt8 function, uInt32 value1, uInt32 value2) { return 0; }
219
220 #ifdef DEBUGGER_SUPPORT
221 /**
222 Get optional debugger widget responsible for displaying info about the cart.
223 This can be used when the debugWidget runs out of space.
224 */
225 virtual CartDebugWidget* infoWidget(GuiObject* boss, const GUI::Font& lfont,
226 const GUI::Font& nfont, int x, int y, int w, int h)
227 {
228 return nullptr;
229 }
230
231 /**
232 Get debugger widget responsible for accessing the inner workings
233 of the cart. This will need to be overridden and implemented by
234 each specific cart type, since the bankswitching/inner workings
235 of each cart type can be very different from each other.
236 */
237 virtual CartDebugWidget* debugWidget(GuiObject* boss, const GUI::Font& lfont,
238 const GUI::Font& nfont, int x, int y, int w, int h)
239 {
240 return nullptr;
241 }
242 #endif
243
244 protected:
245 /**
246 Get a random value to use when a read from the write port happens.
247 Sometimes a RWP means that RAM should be overwritten, sometimes not.
248
249 Internally, this method also keeps track of illegal accesses.
250
251 @param dest The location to place the value, when an overwrite should happen
252 @param address The address of the illegal read
253 @return The value read, whether it is overwritten or not
254 */
255 uInt8 peekRAM(uInt8& dest, uInt16 address);
256
257 /**
258 Use the given value when writing to RAM.
259
260 Internally, this method also keeps track of legal accesses, and removes
261 them from the illegal list.
262
263 @param dest The final location (including address) to place the value
264 @param address The address of the legal write
265 @param value The value to write to the given address
266 */
267 void pokeRAM(uInt8& dest, uInt16 address, uInt8 value);
268
269 /**
270 Create an array that holds code-access information for every byte
271 of the ROM (indicated by 'size'). Note that this is only used by
272 the debugger, and is unavailable otherwise.
273
274 @param size The size of the code-access array to create
275 */
276 void createCodeAccessBase(size_t size);
277
278 /**
279 Fill the given RAM array with (possibly random) data.
280
281 @param arr Pointer to the RAM array
282 @param size The size of the RAM array
283 @param val If provided, the value to store in the RAM array
284 */
285 void initializeRAM(uInt8* arr, size_t size, uInt8 val = 0) const;
286
287 /**
288 Set the start bank to be used when the cart is reset. This method
289 will take both randomization and properties settings into account.
290 See the actual method for more information on the logic used.
291
292 NOTE: If this method is used, it *must* be called from the cart reset()
293 method, *not* from the c'tor.
294
295 @param defaultBank The default bank to use during reset, if
296 randomization or properties aren't being used
297
298 @return The bank number that was determined
299 */
300 uInt16 initializeStartBank(uInt16 defaultBank);
301
302 /**
303 Checks if initial RAM randomization is enabled.
304
305 @return Whether the initial RAM should be randomized
306 */
307 bool randomInitialRAM() const;
308
309 /**
310 Checks if startup bank randomization is enabled.
311
312 @return Whether the startup bank(s) should be randomized
313 */
314 bool randomStartBank() const;
315
316 protected:
317 // Settings class for the application
318 const Settings& mySettings;
319
320 // Indicates if the bank has changed somehow (a bankswitch has occurred)
321 bool myBankChanged;
322
323 // The array containing information about every byte of ROM indicating
324 // whether it is used as code.
325 ByteBuffer myCodeAccessBase;
326
327 // Contains address of illegal RAM write access or 0
328 uInt16 myRamWriteAccess;
329
330 private:
331 // The startup bank to use (where to look for the reset vector address)
332 uInt16 myStartBank;
333
334 // If myBankLocked is true, ignore attempts at bankswitching. This is used
335 // by the debugger, when disassembling/dumping ROM.
336 bool myBankLocked;
337
338 // Semi-random values to use when a read from write port occurs
339 uInt8 myRWPRandomValues[256];
340
341 // Contains various info about this cartridge
342 // This needs to be stored separately from child classes, since
343 // sometimes the information in both do not match
344 // (ie, detected type could be '2in1' while name of cart is '4K')
345 string myAbout, myDetectedType, myMultiCartID;
346
347 // Used when we want the 'Cartridge.StartBank' ROM property
348 StartBankFromPropsFunc myStartBankFromPropsFunc;
349
350 // Contains
351 ShortArray myRAMAccesses;
352
353 // Following constructors and assignment operators not supported
354 Cartridge() = delete;
355 Cartridge(const Cartridge&) = delete;
356 Cartridge(Cartridge&&) = delete;
357 Cartridge& operator=(const Cartridge&) = delete;
358 Cartridge& operator=(Cartridge&&) = delete;
359};
360
361#endif
362