1 | //============================================================================ |
2 | // |
3 | // MM MM 6666 555555 0000 2222 |
4 | // MMMM MMMM 66 66 55 00 00 22 22 |
5 | // MM MMM MM 66 55 00 00 22 |
6 | // MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" |
7 | // MM MM 66 66 55 00 00 22 |
8 | // MM MM 66 66 55 55 00 00 22 |
9 | // MM MM 6666 5555 0000 222222 |
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 SYSTEM_HXX |
19 | #define SYSTEM_HXX |
20 | |
21 | class Device; |
22 | class M6502; |
23 | class M6532; |
24 | class TIA; |
25 | class NullDevice; |
26 | |
27 | #include "bspf.hxx" |
28 | #include "Device.hxx" |
29 | #include "NullDev.hxx" |
30 | #include "Random.hxx" |
31 | #include "Serializable.hxx" |
32 | |
33 | /** |
34 | This class represents a system consisting of a 6502 microprocessor |
35 | and a set of devices. The devices are mapped into an addressing |
36 | space of 2^n bytes (1 <= n <= 16). The addressing space is broken |
37 | into 2^m byte pages (1 <= m <= n), where a page is the smallest unit |
38 | a device can use when installing itself in the system. |
39 | |
40 | In general the addressing space will be 8192 (2^13) bytes for a |
41 | 6507 based system and 65536 (2^16) bytes for a 6502 based system. |
42 | |
43 | @author Bradford W. Mott |
44 | */ |
45 | class System : public Serializable |
46 | { |
47 | public: |
48 | /** |
49 | Create a new system with an addressing space of 2^13 bytes and |
50 | pages of 2^6 bytes. |
51 | */ |
52 | System(Random& random, M6502& m6502, M6532& m6532, |
53 | TIA& mTIA, Cartridge& mCart); |
54 | virtual ~System() = default; |
55 | |
56 | // Mask to apply to an address before accessing memory |
57 | static constexpr uInt16 ADDRESS_MASK = (1 << 13) - 1; |
58 | |
59 | // Amount to shift an address by to determine what page it's on |
60 | static constexpr uInt16 PAGE_SHIFT = 6; |
61 | |
62 | // Size of a page |
63 | static constexpr uInt16 PAGE_SIZE = (1 << PAGE_SHIFT); |
64 | |
65 | // Mask to apply to an address to obtain its page offset |
66 | static constexpr uInt16 PAGE_MASK = PAGE_SIZE - 1; |
67 | |
68 | // Number of pages in the system |
69 | static constexpr uInt16 NUM_PAGES = 1 << (13 - PAGE_SHIFT); |
70 | |
71 | public: |
72 | /** |
73 | Initialize system and all attached devices to known state. |
74 | */ |
75 | void initialize(); |
76 | |
77 | /** |
78 | Reset the system cycle counter, the attached devices, and the |
79 | attached processor of the system. |
80 | |
81 | @param autodetect A hint to devices that the system is currently |
82 | in autodetect mode. That is, the system is being |
83 | run to autodetect certain device settings before |
84 | actual emulation will begin. Certain devices may |
85 | use this hint to act differently under those |
86 | circumstances. |
87 | */ |
88 | void reset(bool autodetect = false); |
89 | |
90 | public: |
91 | /** |
92 | Answer the 6502 microprocessor attached to the system. If a |
93 | processor has not been attached calling this function will fail. |
94 | |
95 | @return The attached 6502 microprocessor |
96 | */ |
97 | M6502& m6502() const { return myM6502; } |
98 | |
99 | /** |
100 | Answer the 6532 processor attached to the system. If a |
101 | processor has not been attached calling this function will fail. |
102 | |
103 | @return The attached 6532 microprocessor |
104 | */ |
105 | M6532& m6532() const { return myM6532; } |
106 | |
107 | /** |
108 | Answer the TIA device attached to the system. |
109 | |
110 | @return The attached TIA device |
111 | */ |
112 | TIA& tia() const { return myTIA; } |
113 | |
114 | /** |
115 | Answer the Cart attached to the system. |
116 | |
117 | @return The attached cartridge |
118 | */ |
119 | Cartridge& cart() const { return myCart; } |
120 | |
121 | /** |
122 | Answer the random generator attached to the system. |
123 | |
124 | @return The random generator |
125 | */ |
126 | Random& randGenerator() const { return myRandom; } |
127 | |
128 | /** |
129 | Get the null device associated with the system. Every system |
130 | has a null device associated with it that's used by pages which |
131 | aren't mapped to "real" devices. |
132 | |
133 | @return The null device associated with the system |
134 | */ |
135 | const NullDevice& nullDevice() const { return myNullDevice; } |
136 | |
137 | public: |
138 | /** |
139 | Get the number of system cycles which have passed since the |
140 | system was created. |
141 | |
142 | @return The number of system cycles which have passed |
143 | */ |
144 | uInt64 cycles() const { return myCycles; } |
145 | |
146 | /** |
147 | Increment the system cycles by the specified number of cycles. |
148 | |
149 | @param amount The amount to add to the system cycles counter |
150 | */ |
151 | void incrementCycles(uInt32 amount) { myCycles += amount; } |
152 | |
153 | /** |
154 | Informs all attached devices that the console type has changed. |
155 | */ |
156 | void consoleChanged(ConsoleTiming timing); |
157 | |
158 | /** |
159 | Answers whether the system is currently in device autodetect mode. |
160 | */ |
161 | bool autodetectMode() const { return mySystemInAutodetect; } |
162 | |
163 | public: |
164 | /** |
165 | Get the current state of the data bus in the system. The current |
166 | state is the last data that was accessed by the system. |
167 | |
168 | @return The data bus state |
169 | */ |
170 | uInt8 getDataBusState() const { return myDataBusState; } |
171 | |
172 | /** |
173 | Get the current state of the data bus in the system, taking into |
174 | account that certain bits are in Z-state (undriven). In those |
175 | cases, the bits are floating, but will usually be the same as the |
176 | last data bus value (the 'usually' is emulated by randomly driving |
177 | certain bits high). |
178 | |
179 | However, some CMOS EPROM chips always drive Z-state bits high. |
180 | This is emulated by hmask, which specifies to push a specific |
181 | Z-state bit high. |
182 | |
183 | @param zmask The bits which are in Z-state |
184 | @param hmask The bits which should always be driven high |
185 | @return The data bus state |
186 | */ |
187 | uInt8 getDataBusState(uInt8 zmask, uInt8 hmask = 0x00) const |
188 | { |
189 | // For the pins that are floating, randomly decide which are high or low |
190 | // Otherwise, they're specifically driven high |
191 | return (myDataBusState | (randGenerator().next() | hmask)) & zmask; |
192 | } |
193 | |
194 | /** |
195 | Get the byte at the specified address. No masking of the |
196 | address occurs before it's sent to the device mapped at |
197 | the address. |
198 | |
199 | @param address The address from which the value should be loaded |
200 | @param flags Indicates that this address has the given flags |
201 | for type of access (CODE, DATA, GFX, etc) |
202 | |
203 | @return The byte at the specified address |
204 | */ |
205 | uInt8 peek(uInt16 address, uInt8 flags = 0); |
206 | |
207 | /** |
208 | Change the byte at the specified address to the given value. |
209 | No masking of the address occurs before it's sent to the device |
210 | mapped at the address. |
211 | |
212 | This method sets the 'page dirty' if the write succeeds. In the |
213 | case of direct-access pokes, the write always succeeds. Otherwise, |
214 | if the device is handling the poke, we depend on its return value |
215 | for this information. |
216 | |
217 | @param address The address where the value should be stored |
218 | @param value The value to be stored at the address |
219 | */ |
220 | void poke(uInt16 address, uInt8 value, uInt8 flags = 0); |
221 | |
222 | /** |
223 | Lock/unlock the data bus. When the bus is locked, peek() and |
224 | poke() don't update the bus state. The bus should be unlocked |
225 | while the CPU is running (normal emulation, or when the debugger |
226 | is stepping/advancing). It should be locked while the debugger |
227 | is active but not running the CPU. This is so the debugger can |
228 | use System.peek() to examine memory/registers without changing |
229 | the state of the system. |
230 | */ |
231 | void lockDataBus() { myDataBusLocked = true; } |
232 | void unlockDataBus() { myDataBusLocked = false; } |
233 | |
234 | /** |
235 | Access and modify the disassembly type flags for the given |
236 | address. Note that while any flag can be used, the disassembly |
237 | only really acts on CODE/GFX/PGFX/DATA/ROW. |
238 | */ |
239 | uInt8 getAccessFlags(uInt16 address) const; |
240 | void setAccessFlags(uInt16 address, uInt8 flags); |
241 | |
242 | public: |
243 | /** |
244 | Describes how a page can be accessed |
245 | */ |
246 | enum class PageAccessType : uInt8 { |
247 | READ = 1 << 0, |
248 | WRITE = 1 << 1, |
249 | READWRITE = READ | WRITE |
250 | }; |
251 | |
252 | /** |
253 | Structure used to specify access methods for a page |
254 | */ |
255 | struct PageAccess |
256 | { |
257 | /** |
258 | Pointer to a block of memory or the null pointer. The null pointer |
259 | indicates that the device's peek method should be invoked for reads |
260 | to this page, while other values are the base address of an array |
261 | to directly access for reads to this page. |
262 | */ |
263 | uInt8* directPeekBase; |
264 | |
265 | /** |
266 | Pointer to a block of memory or the null pointer. The null pointer |
267 | indicates that the device's poke method should be invoked for writes |
268 | to this page, while other values are the base address of an array |
269 | to directly access for pokes to this page. |
270 | */ |
271 | uInt8* directPokeBase; |
272 | |
273 | /** |
274 | Pointer to a lookup table for marking an address as CODE. A CODE |
275 | section is defined as any address that appears in the program |
276 | counter. Currently, this is used by the debugger/disassembler to |
277 | conclusively determine if a section of address space is CODE, even |
278 | if the disassembler failed to mark it as such. |
279 | */ |
280 | uInt8* codeAccessBase; |
281 | |
282 | /** |
283 | Pointer to the device associated with this page or to the system's |
284 | null device if the page hasn't been mapped to a device. |
285 | */ |
286 | Device* device; |
287 | |
288 | /** |
289 | The manner in which the pages are accessed by the system |
290 | (READ, WRITE, READWRITE) |
291 | */ |
292 | PageAccessType type; |
293 | |
294 | // Constructors |
295 | PageAccess() |
296 | : directPeekBase(nullptr), |
297 | directPokeBase(nullptr), |
298 | codeAccessBase(nullptr), |
299 | device(nullptr), |
300 | type(System::PageAccessType::READ) { } |
301 | |
302 | PageAccess(Device* dev, PageAccessType access) |
303 | : directPeekBase(nullptr), |
304 | directPokeBase(nullptr), |
305 | codeAccessBase(nullptr), |
306 | device(dev), |
307 | type(access) { } |
308 | }; |
309 | |
310 | /** |
311 | Set the page accessing method for the specified address. |
312 | |
313 | @param addr The address/page accessing methods should be set for |
314 | @param access The accessing methods to be used by the page |
315 | */ |
316 | void setPageAccess(uInt16 addr, const PageAccess& access) { |
317 | myPageAccessTable[(addr & ADDRESS_MASK) >> PAGE_SHIFT] = access; |
318 | } |
319 | |
320 | /** |
321 | Get the page accessing method for the specified address. |
322 | |
323 | @param addr The address/page to get accessing methods for |
324 | @return The accessing methods used by the page |
325 | */ |
326 | const PageAccess& getPageAccess(uInt16 addr) const { |
327 | return myPageAccessTable[(addr & ADDRESS_MASK) >> PAGE_SHIFT]; |
328 | } |
329 | |
330 | /** |
331 | Get the page type for the given address. |
332 | |
333 | @param addr The address contained in the page in questions |
334 | @return The type of page that contains the given address |
335 | */ |
336 | System::PageAccessType getPageAccessType(uInt16 addr) const { |
337 | return myPageAccessTable[(addr & ADDRESS_MASK) >> PAGE_SHIFT].type; |
338 | } |
339 | |
340 | /** |
341 | Mark the page containing this address as being dirty. |
342 | |
343 | @param addr Determines the page that is dirty |
344 | */ |
345 | void setDirtyPage(uInt16 addr) { |
346 | myPageIsDirtyTable[(addr & ADDRESS_MASK) >> PAGE_SHIFT] = true; |
347 | } |
348 | |
349 | /** |
350 | Answer whether any pages in given range of addresses have been |
351 | marked as dirty. |
352 | |
353 | @param start_addr The start address; determines the start page |
354 | @param end_addr The end address; determines the end page |
355 | */ |
356 | bool isPageDirty(uInt16 start_addr, uInt16 end_addr) const; |
357 | |
358 | /** |
359 | Mark all pages as clean (ie, turn off the dirty flag). |
360 | */ |
361 | void clearDirtyPages(); |
362 | |
363 | /** |
364 | Save the current state of this system to the given Serializer. |
365 | |
366 | @param out The Serializer object to use |
367 | @return False on any errors, else true |
368 | */ |
369 | bool save(Serializer& out) const override; |
370 | |
371 | /** |
372 | Load the current state of this system from the given Serializer. |
373 | |
374 | @param in The Serializer object to use |
375 | @return False on any errors, else true |
376 | */ |
377 | bool load(Serializer& in) override; |
378 | |
379 | private: |
380 | // The system RNG |
381 | Random& myRandom; |
382 | |
383 | // 6502 processor attached to the system |
384 | M6502& myM6502; |
385 | |
386 | // 6532 processor attached to the system |
387 | M6532& myM6532; |
388 | |
389 | // TIA device attached to the system |
390 | TIA& myTIA; |
391 | |
392 | // Cartridge device attached to the system |
393 | Cartridge& myCart; |
394 | |
395 | // Number of system cycles executed since last reset |
396 | uInt64 myCycles; |
397 | |
398 | // Null device to use for page which are not installed |
399 | NullDevice myNullDevice; |
400 | |
401 | // The list of PageAccess structures |
402 | std::array<PageAccess, NUM_PAGES> myPageAccessTable; |
403 | |
404 | // The list of dirty pages |
405 | std::array<bool, NUM_PAGES> myPageIsDirtyTable; |
406 | |
407 | // The current state of the Data Bus |
408 | uInt8 myDataBusState; |
409 | |
410 | // Whether or not peek() updates the data bus state. This |
411 | // is true during normal emulation, and false when the |
412 | // debugger is active. |
413 | bool myDataBusLocked; |
414 | |
415 | // Whether autodetection is currently running (ie, the emulation |
416 | // core is attempting to autodetect display settings, cart modes, etc) |
417 | // Some parts of the codebase need to act differently in such a case |
418 | bool mySystemInAutodetect; |
419 | |
420 | private: |
421 | // Following constructors and assignment operators not supported |
422 | System() = delete; |
423 | System(const System&) = delete; |
424 | System(System&&) = delete; |
425 | System& operator=(const System&) = delete; |
426 | System& operator=(System&&) = delete; |
427 | }; |
428 | |
429 | #endif |
430 | |