VDPVRAM.hh 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. #ifndef VDPVRAM_HH
  2. #define VDPVRAM_HH
  3. #include "VRAMObserver.hh"
  4. #include "VDP.hh"
  5. #include "VDPCmdEngine.hh"
  6. #include "SimpleDebuggable.hh"
  7. #include "Ram.hh"
  8. #include "Math.hh"
  9. #include "openmsx.hh"
  10. #include "likely.hh"
  11. #include <cassert>
  12. namespace openmsx {
  13. class DisplayMode;
  14. class SpriteChecker;
  15. class Renderer;
  16. /*
  17. Note: The way VRAM is accessed depends a lot on who is doing the accessing.
  18. For example, the ranges:
  19. - Table access is done using masks.
  20. - Command engine work areas are rectangles.
  21. - CPU access always spans full memory.
  22. Maybe define an interface with multiple subclasses?
  23. Or is that too much of a performance hit?
  24. If accessed through the interface, a virtual method call is made.
  25. But invoking the objects directly should be inlined.
  26. Timing:
  27. Each window reflects the state of the VRAM at a specified moment in time.
  28. Because the CPU has full-range write access, it is incorrect for any window
  29. to be ahead in time compared to the CPU. Because multi-cycle operations are
  30. implemented as atomic, it is currently possible that a window which starts
  31. an operation slightly before CPU time ends up slightly after CPU time.
  32. Solutions:
  33. - break up operations in 1-cycle suboperations
  34. (very hard to reverse engineer accurately)
  35. - do not start an operation until its end time is after CPU time
  36. (requires minor rewrite of command engine)
  37. - make the code that uses the timestamps resilient to after-CPU times
  38. (current implementation; investigate if this is correct)
  39. Window ranges are not at fixed. But they can only be changed by the CPU, so
  40. they are fixed until CPU time, which subsystems will never go beyond anyway.
  41. The only two subsystems with write access are CPU and command engine.
  42. The command engine can only start executing a new command if instructed so
  43. by the CPU. Therefore it is known which area the command engine can write
  44. in until CPU time:
  45. - empty, if the command engine is not executing a command
  46. - the command's reach, if the command engine is executing a command
  47. Currently the command's reach is not computed: full VRAM is used.
  48. Taking the Y coordinate into account would speed things up a lot, because
  49. usually commands execute on invisible pages, so the number of area overlaps
  50. between renderer and command engine would be reduced significantly.
  51. Also sprite tables are usually not written by commands.
  52. Reading through a window is done as follows:
  53. A subsystem reads the VRAM because it is updating itself to a certain moment
  54. in time T.
  55. 1. the subsystems syncs the window to T
  56. 2. VDPVRAM checks overlap of the window with the command write area
  57. no overlap -> go to step 6
  58. 3. VDPVRAM syncs the command engine to T
  59. 4. the command engine calls VDPVRAM to write each byte it changes in VRAM,
  60. call the times this happens C1, C2, C3...
  61. 5. at the n-th write, VDPVRAM updates any subsystem with the written address
  62. in its window to Cn, this can include the original subsystem
  63. 6. the window has reached T
  64. now the subsystem can update itself to T
  65. Using this approach instead of syncing on read makes sure there is no
  66. re-entrance on the subsystem update methods.
  67. Note: command engine reads through write window when doing logic-ops.
  68. So "source window" and "destination window" would be better names.
  69. Interesting observation:
  70. Each window is at the same moment in time as the command engine (C):
  71. - if a window doesn't overlap with the command destination window, it is
  72. stable from a moment before C until the CPU time T
  73. - if a window overlaps with the command destination window, it cannot be
  74. before C (incorrect) or after C (uncertainty)
  75. Since there is only one time for the entire VRAM, the VRAM itself can be said
  76. to be at C. This is a justification for having the sync method in VDPVRAM
  77. instead of in Window.
  78. Writing through a window is done as follows:
  79. - CPU write: sync with all non-CPU windows, including command engine write
  80. - command engine write: sync with non-CPU and non-command engine windows
  81. Syncing with a window is only necessary if the write falls into that window.
  82. If all non-CPU windows are disjunct, then all subsystems function
  83. independently (at least until CPU time), no need for syncs.
  84. So what is interesting, is which windows overlap.
  85. Since windows change position infrequently, it may be beneficial to
  86. precalculate overlaps.
  87. Not necessarily though, because even if two windows overlap, a single write
  88. may not be inside the other window. So precalculated overlaps only speeds up
  89. in the case there is no overlap.
  90. Maybe it's not necessary to know exactly which windows overlap with cmdwrite,
  91. only to know whether there are any. If not, sync can be skipped.
  92. Is it possible to read multiple bytes at the same time?
  93. In other words, get a pointer to an array instead of reading single bytes.
  94. Yes, but only the first 64 bytes are guaranteed to be correct, because that
  95. is the granularity of the color table.
  96. But since whatever is reading the VRAM knows what it is operating on, it
  97. can decide for itself how many bytes to read.
  98. */
  99. class DummyVRAMOBserver final : public VRAMObserver
  100. {
  101. public:
  102. void updateVRAM(unsigned /*offset*/, EmuTime::param /*time*/) override {}
  103. void updateWindow(bool /*enabled*/, EmuTime::param /*time*/) override {}
  104. };
  105. /** Specifies an address range in the VRAM.
  106. * A VDP subsystem can use this to put a claim on a certain area.
  107. * For example, the owner of a read window will be notified before
  108. * writes to the corresponding area are commited.
  109. * The address range is specified by a mask and is not necessarily
  110. * continuous. See "doc/vram-addressing.txt" for details.
  111. * TODO: Rename to "Table"? That's the term the VDP data book uses.
  112. * Maybe have two classes: "Table" for tables, using a mask,
  113. * and "Window" for the command engine, using an interval.
  114. */
  115. class VRAMWindow
  116. {
  117. public:
  118. VRAMWindow(const VRAMWindow&) = delete;
  119. VRAMWindow& operator=(const VRAMWindow&) = delete;
  120. /** Gets the mask for this window.
  121. * Should only be called if the window is enabled.
  122. * TODO: Only used by dirty checking. Maybe a new dirty checking
  123. * approach can obsolete this method?
  124. */
  125. inline int getMask() const {
  126. assert(isEnabled());
  127. return effectiveBaseMask;
  128. }
  129. /** Sets the mask and enables this window.
  130. * @param newBaseMask The table base register,
  131. * with the unused bits all ones.
  132. * @param newIndexMask The table index mask,
  133. * with the unused bits all ones.
  134. * @param time The moment in emulated time this change occurs.
  135. * TODO: In planar mode, the index bits are rotated one to the right.
  136. * Solution: have the caller pass index mask instead of the
  137. * number of bits.
  138. * For many tables the number of index bits depends on the
  139. * display mode anyway.
  140. */
  141. inline void setMask(int newBaseMask, int newIndexMask,
  142. EmuTime::param time) {
  143. origBaseMask = newBaseMask;
  144. newBaseMask &= sizeMask;
  145. if (isEnabled() &&
  146. (newBaseMask == effectiveBaseMask) &&
  147. (newIndexMask == indexMask)) {
  148. return;
  149. }
  150. observer->updateWindow(true, time);
  151. effectiveBaseMask = newBaseMask;
  152. indexMask = newIndexMask;
  153. baseAddr = effectiveBaseMask & indexMask; // this enables window
  154. combiMask = ~effectiveBaseMask | indexMask;
  155. }
  156. /** Disable this window: no address will be considered inside.
  157. * @param time The moment in emulated time this change occurs.
  158. */
  159. inline void disable(EmuTime::param time) {
  160. observer->updateWindow(false, time);
  161. baseAddr = -1;
  162. }
  163. /** Is the given index range continuous in VRAM (iow there's no mirroring)
  164. * Only if the range is continuous it's allowed to call getReadArea().
  165. */
  166. inline bool isContinuous(unsigned index, unsigned size) const {
  167. assert(isEnabled());
  168. unsigned endIndex = index + size - 1;
  169. unsigned areaBits = Math::floodRight(index ^ endIndex);
  170. if ((areaBits & effectiveBaseMask) != areaBits) return false;
  171. if ((areaBits & ~indexMask) != areaBits) return false;
  172. return true;
  173. }
  174. /** Alternative version to check whether a region is continuous in
  175. * VRAM. This tests whether all addresses in the range
  176. * 0bCCCCCCCCCCXXXXXXXX (with C constant and X varying)
  177. * are continuous. The input must be a value 1-less-than-a-power-of-2
  178. * (so a binary value containing zeros on the left ones on the right)
  179. * 1-bits in the parameter correspond with 'X' in the pattern above.
  180. * Or IOW it tests an aligned-power-of-2-sized region.
  181. */
  182. inline bool isContinuous(unsigned mask) const {
  183. assert(isEnabled());
  184. assert((mask & ~indexMask) == mask);
  185. return (mask & effectiveBaseMask) == mask;
  186. }
  187. /** Gets a pointer to a contiguous part of the VRAM. The region is
  188. * [index, index + size) inside the current window.
  189. * @param index Index in table
  190. * @param size Size of the block. This is only used to assert that
  191. * requested block is not too large.
  192. */
  193. inline const byte* getReadArea(unsigned index, unsigned size) const {
  194. assert(isContinuous(index, size)); (void)size;
  195. return &data[effectiveBaseMask & (indexMask | index)];
  196. }
  197. /** Similar to getReadArea(), but now with planar addressing mode.
  198. * This means the region is split in two: one region for the even bytes
  199. * (ptr0) and another for the odd bytes (ptr1).
  200. * @param index Index in table
  201. * @param size Size of the block. This is only used to assert that
  202. * requested block is not too large.
  203. * @param ptr0 out Pointer to the block of even numbered bytes.
  204. * @param ptr1 out Pointer to the block of odd numbered bytes.
  205. */
  206. inline void getReadAreaPlanar(
  207. unsigned index, unsigned size,
  208. const byte*& ptr0, const byte*& ptr1) const {
  209. assert((index & 1) == 0);
  210. assert((size & 1) == 0);
  211. unsigned endIndex = index + size - 1;
  212. unsigned areaBits = Math::floodRight(index ^ endIndex);
  213. areaBits = ((areaBits << 16) | (areaBits >> 1)) & 0x1FFFF;
  214. (void)areaBits;
  215. assert((areaBits & effectiveBaseMask) == areaBits);
  216. assert((areaBits & ~indexMask) == areaBits);
  217. assert(isEnabled());
  218. unsigned addr = effectiveBaseMask & (indexMask | (index >> 1));
  219. ptr0 = &data[addr | 0x00000];
  220. ptr1 = &data[addr | 0x10000];
  221. }
  222. /** Reads a byte from VRAM in its current state.
  223. * @param index Index in table, with unused bits set to 1.
  224. */
  225. inline byte readNP(unsigned index) const {
  226. assert(isEnabled());
  227. return data[effectiveBaseMask & index];
  228. }
  229. /** Similar to readNP, but now with planar addressing.
  230. * @param index Index in table, with unused bits set to 1.
  231. */
  232. inline byte readPlanar(unsigned index) const {
  233. assert(isEnabled());
  234. index = ((index & 1) << 16) | ((index & 0x1FFFE) >> 1);
  235. unsigned addr = effectiveBaseMask & index;
  236. return data[addr];
  237. }
  238. /** Is there an observer registered for this window?
  239. */
  240. inline bool hasObserver() const {
  241. return observer != &dummyObserver;
  242. }
  243. /** Register an observer on this VRAM window.
  244. * It will be called when changes occur within the window.
  245. * There can be only one observer per window at any given time.
  246. * @param newObserver The observer to register.
  247. */
  248. inline void setObserver(VRAMObserver* newObserver) {
  249. observer = newObserver;
  250. }
  251. /** Unregister the observer of this VRAM window.
  252. */
  253. inline void resetObserver() {
  254. observer = &dummyObserver;
  255. }
  256. /** Test whether an address is inside this window.
  257. * "Inside" is defined as: there is at least one index in this window,
  258. * which is mapped to the given address.
  259. * TODO: Might be replaced by notify().
  260. * @param address The address to test.
  261. * @return true iff the address is inside this window.
  262. */
  263. inline bool isInside(unsigned address) const {
  264. return (address & combiMask) == unsigned(baseAddr);
  265. }
  266. /** Notifies the observer of this window of a VRAM change,
  267. * if the changes address is inside this window.
  268. * @param address The address to test.
  269. * @param time The moment in emulated time the change occurs.
  270. */
  271. inline void notify(unsigned address, EmuTime::param time) {
  272. if (isInside(address)) {
  273. observer->updateVRAM(address - baseAddr, time);
  274. }
  275. }
  276. /** Inform VRAMWindow of changed sizeMask.
  277. * For the moment this only happens when switching the VR bit in VDP
  278. * register 8 (in VR=0 mode only 32kB VRAM is addressable).
  279. */
  280. void setSizeMask(unsigned newSizeMask, EmuTime::param time) {
  281. sizeMask = newSizeMask;
  282. if (isEnabled()) {
  283. setMask(origBaseMask, indexMask, time);
  284. }
  285. }
  286. template<typename Archive>
  287. void serialize(Archive& ar, unsigned version);
  288. private:
  289. inline bool isEnabled() const {
  290. return baseAddr != -1;
  291. }
  292. /** Only VDPVRAM may construct VRAMWindow objects.
  293. */
  294. friend class VDPVRAM;
  295. /** Create a new window.
  296. * Initially, the window is disabled; use setRange to enable it.
  297. */
  298. explicit VRAMWindow(Ram& vram);
  299. /** Pointer to the entire VRAM data.
  300. */
  301. byte* data;
  302. /** Observer associated with this VRAM window.
  303. * It will be called when changes occur within the window.
  304. * If there is no observer, this variable is &dummyObserver.
  305. */
  306. VRAMObserver* observer;
  307. /** Base mask as passed to the setMask() method.
  308. */
  309. int origBaseMask;
  310. /** Effective mask of this window.
  311. * This is always equal to 'origBaseMask & sizeMask'.
  312. */
  313. int effectiveBaseMask;
  314. /** Index mask of this window.
  315. */
  316. int indexMask;
  317. /** Lowest address in this window.
  318. * Or -1 when this window is disabled.
  319. */
  320. int baseAddr;
  321. /** Combination of effectiveBaseMask and index mask used for "inside" checks.
  322. */
  323. int combiMask;
  324. /** Mask to handle vram mirroring
  325. * Note: this only handles mirroring for power-of-2 sizes
  326. * mirroring of extended VRAM is handled in a different way
  327. */
  328. int sizeMask;
  329. static inline DummyVRAMOBserver dummyObserver;
  330. };
  331. /** Manages VRAM contents and synchronises the various users of the VRAM.
  332. * VDPVRAM does not apply planar remapping to addresses, this is the
  333. * responsibility of the caller.
  334. */
  335. class VDPVRAM
  336. {
  337. public:
  338. VDPVRAM(const VDPVRAM&) = delete;
  339. VDPVRAM& operator=(const VDPVRAM&) = delete;
  340. VDPVRAM(VDP& vdp, unsigned size, EmuTime::param time);
  341. /** Initialize VRAM content to power-up state.
  342. */
  343. void clear();
  344. /** Update VRAM state to specified moment in time.
  345. * @param time Moment in emulated time to update VRAM to.
  346. * TODO: Replace this method by VRAMWindow::sync().
  347. */
  348. inline void sync(EmuTime::param time) {
  349. assert(vdp.isInsideFrame(time));
  350. cmdEngine->sync(time);
  351. }
  352. /** Write a byte from the command engine.
  353. * Synchronisation with reads by the command engine is skipped.
  354. * TODO: Replace by "cmdSync ; VRAMWindow::write".
  355. * Note: "cmdSync", because it checks against read windows, unlike
  356. * the other sync which checks against the cmd write window.
  357. */
  358. inline void cmdWrite(unsigned address, byte value, EmuTime::param time) {
  359. #ifdef DEBUG
  360. // Rewriting history is not allowed.
  361. assert(time >= vramTime);
  362. #endif
  363. assert(vdp.isInsideFrame(time));
  364. // handle mirroring and non-present ram chips
  365. address &= sizeMask;
  366. if (unlikely(address >= actualSize)) {
  367. // 192kb vram is mirroring is handled elsewhere
  368. assert(address < 0x30000);
  369. // only happens in case of 16kb vram while you write
  370. // to range [0x4000,0x8000)
  371. return;
  372. }
  373. writeCommon(address, value, time);
  374. }
  375. /** Write a byte to VRAM through the CPU interface.
  376. * @param address The address to write.
  377. * @param value The value to write.
  378. * @param time The moment in emulated time this write occurs.
  379. */
  380. inline void cpuWrite(unsigned address, byte value, EmuTime::param time) {
  381. #ifdef DEBUG
  382. // Rewriting history is not allowed.
  383. assert(time >= vramTime);
  384. #endif
  385. assert(vdp.isInsideFrame(time));
  386. // handle mirroring and non-present ram chips
  387. address &= sizeMask;
  388. if (unlikely(address >= actualSize)) {
  389. // 192kb vram is mirroring is handled elsewhere
  390. assert(address < 0x30000);
  391. // only happens in case of 16kb vram while you write
  392. // to range [0x4000,0x8000)
  393. return;
  394. }
  395. // We should still sync with cmdEngine, even if the VRAM already
  396. // contains the value we're about to write (e.g. it's possible
  397. // syncing with cmdEngine changes that value, and this write
  398. // restores it again). This fixes bug:
  399. // [2844043] Hinotori - Firebird small graphics corruption
  400. if (cmdReadWindow .isInside(address) ||
  401. cmdWriteWindow.isInside(address)) {
  402. cmdEngine->sync(time);
  403. }
  404. writeCommon(address, value, time);
  405. cmdEngine->stealAccessSlot(time);
  406. }
  407. /** Read a byte from VRAM though the CPU interface.
  408. * @param address The address to read.
  409. * @param time The moment in emulated time this read occurs.
  410. * @return The VRAM contents at the specified address.
  411. */
  412. inline byte cpuRead(unsigned address, EmuTime::param time) {
  413. #ifdef DEBUG
  414. // VRAM should never get ahead of CPU.
  415. assert(time >= vramTime);
  416. #endif
  417. assert(vdp.isInsideFrame(time));
  418. address &= sizeMask;
  419. if (cmdWriteWindow.isInside(address)) {
  420. cmdEngine->sync(time);
  421. }
  422. cmdEngine->stealAccessSlot(time);
  423. #ifdef DEBUG
  424. vramTime = time;
  425. #endif
  426. return data[address];
  427. }
  428. /** Used by the VDP to signal display mode changes.
  429. * VDPVRAM will inform the Renderer, command engine and the sprite
  430. * checker of this change.
  431. * TODO: Does this belong in VDPVRAM?
  432. * @param mode The new display mode.
  433. * @param cmdBit Are VDP commands allowed in non-bitmap mode.
  434. * @param time The moment in emulated time this change occurs.
  435. */
  436. void updateDisplayMode(DisplayMode mode, bool cmdBit, EmuTime::param time);
  437. /** Used by the VDP to signal display enabled changes.
  438. * Both the regular border start/end and forced blanking by clearing
  439. * the display enable bit are considered display enabled changes.
  440. * @param enabled The new display enabled state.
  441. * @param time The moment in emulated time this change occurs.
  442. */
  443. void updateDisplayEnabled(bool enabled, EmuTime::param time);
  444. /** Used by the VDP to signal sprites enabled changes.
  445. * @param enabled The new sprites enabled state.
  446. * @param time The moment in emulated time this change occurs.
  447. */
  448. void updateSpritesEnabled(bool enabled, EmuTime::param time);
  449. /** Change between VR=0 and VR=1 mode.
  450. * @param mode false->VR=0 true->VR=1
  451. * @param time The moment in emulated time this change occurs.
  452. */
  453. void updateVRMode(bool mode, EmuTime::param time);
  454. void setRenderer(Renderer* renderer, EmuTime::param time);
  455. /** Returns the size of VRAM in bytes
  456. */
  457. unsigned getSize() const {
  458. return actualSize;
  459. }
  460. /** Necessary because of circular dependencies.
  461. */
  462. inline void setSpriteChecker(SpriteChecker* newSpriteChecker) {
  463. spriteChecker = newSpriteChecker;
  464. }
  465. /** Necessary because of circular dependencies.
  466. */
  467. inline void setCmdEngine(VDPCmdEngine* newCmdEngine) {
  468. cmdEngine = newCmdEngine;
  469. }
  470. /** TMS99x8 VRAM can be mapped in two ways.
  471. * See implementation for more details.
  472. */
  473. void change4k8kMapping(bool mapping8k);
  474. template<typename Archive>
  475. void serialize(Archive& ar, unsigned version);
  476. private:
  477. /* Common code of cmdWrite() and cpuWrite()
  478. */
  479. inline void writeCommon(unsigned address, byte value, EmuTime::param time) {
  480. #ifdef DEBUG
  481. assert(time >= vramTime);
  482. vramTime = time;
  483. #endif
  484. // Check that VRAM will actually be changed.
  485. // A lot of costly syncs can be saved if the same value is written.
  486. // For example Penguin Adventure always uploads the whole frame,
  487. // even if it is the same as the previous frame.
  488. if (data[address] == value) return;
  489. // Subsystem synchronisation should happen before the commit,
  490. // to be able to draw backlog using old state.
  491. bitmapVisibleWindow.notify(address, time);
  492. spriteAttribTable.notify(address, time);
  493. spritePatternTable.notify(address, time);
  494. data[address] = value;
  495. // Cache dirty marking should happen after the commit,
  496. // otherwise the cache could be re-validated based on old state.
  497. // these two seem to be unused
  498. // bitmapCacheWindow.notify(address, time);
  499. // nameTable.notify(address, time);
  500. assert(!bitmapCacheWindow.hasObserver());
  501. assert(!nameTable.hasObserver());
  502. // in the past GLRasterizer observed these two, now there are none
  503. assert(!colorTable.hasObserver());
  504. assert(!patternTable.hasObserver());
  505. /* TODO:
  506. There seems to be a significant difference between subsystem sync
  507. and cache admin. One example is the code above, the other is
  508. updateWindow, where subsystem sync is interested in windows that
  509. were enabled before (new state doesn't matter), while cache admin
  510. is interested in windows that become enabled (old state doesn't
  511. matter).
  512. Does this mean it makes sense to have separate VRAMWindow like
  513. classes for each category?
  514. Note: In the future, sprites may switch category, or fall in both.
  515. */
  516. }
  517. void setSizeMask(EmuTime::param time);
  518. /** VDP this VRAM belongs to.
  519. */
  520. VDP& vdp;
  521. /** VRAM data block.
  522. */
  523. Ram data;
  524. /** Debuggable with mode dependend view on the vram
  525. * Screen7/8 are not interleaved in this mode.
  526. * This debuggable is also at least 128kB in size (it possibly
  527. * contains unmapped regions).
  528. */
  529. class LogicalVRAMDebuggable final : public SimpleDebuggable {
  530. public:
  531. explicit LogicalVRAMDebuggable(VDP& vdp);
  532. byte read(unsigned address, EmuTime::param time) override;
  533. void write(unsigned address, byte value, EmuTime::param time) override;
  534. private:
  535. unsigned transform(unsigned address);
  536. } logicalVRAMDebug;
  537. /** Physical view on the VRAM.
  538. * Screen 7/8 are interleaved in this mode. The size of this
  539. * debuggable is the same as the actual VRAM size.
  540. */
  541. struct PhysicalVRAMDebuggable final : SimpleDebuggable {
  542. PhysicalVRAMDebuggable(VDP& vdp, unsigned actualSize);
  543. byte read(unsigned address, EmuTime::param time) override;
  544. void write(unsigned address, byte value, EmuTime::param time) override;
  545. } physicalVRAMDebug;
  546. // TODO: Renderer field can be removed, if updateDisplayMode
  547. // and updateDisplayEnabled are moved back to VDP.
  548. // Is that a good idea?
  549. Renderer* renderer;
  550. VDPCmdEngine* cmdEngine;
  551. SpriteChecker* spriteChecker;
  552. /** The last time a CmdEngine write or a CPU read/write occured.
  553. * This is only used in a debug build to check if read/writes come
  554. * in the correct order.
  555. */
  556. #ifdef DEBUG
  557. EmuTime vramTime;
  558. #endif
  559. /** Mask to handle vram mirroring
  560. * Note: this only handles mirroring at power-of-2 sizes
  561. * mirroring of extended VRAM is handled in a different way
  562. */
  563. unsigned sizeMask;
  564. /** Actual size of VRAM. Normally this is in sync with sizeMask, but
  565. * for 16kb VRAM sizeMask is 32kb-1 while actualSize is only 16kb.
  566. */
  567. const unsigned actualSize;
  568. /** Corresponds to the VR bit (bit 3 in VDP register 8).
  569. */
  570. bool vrMode;
  571. public:
  572. VRAMWindow cmdReadWindow;
  573. VRAMWindow cmdWriteWindow;
  574. VRAMWindow nameTable;
  575. VRAMWindow colorTable;
  576. VRAMWindow patternTable;
  577. VRAMWindow bitmapVisibleWindow;
  578. VRAMWindow bitmapCacheWindow;
  579. VRAMWindow spriteAttribTable;
  580. VRAMWindow spritePatternTable;
  581. };
  582. } // namespace openmsx
  583. #endif