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 | |
21 | class Cartridge; |
22 | class Properties; |
23 | class CartDebugWidget; |
24 | class CartRamWidget; |
25 | class 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 | */ |
42 | class 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 | |