| 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 | |