VDP.hh 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250
  1. #ifndef VDP_HH
  2. #define VDP_HH
  3. #include "MSXDevice.hh"
  4. #include "Schedulable.hh"
  5. #include "VideoSystemChangeListener.hh"
  6. #include "SimpleDebuggable.hh"
  7. #include "TclCallback.hh"
  8. #include "InfoTopic.hh"
  9. #include "IRQHelper.hh"
  10. #include "Clock.hh"
  11. #include "DisplayMode.hh"
  12. #include "Observer.hh"
  13. #include "openmsx.hh"
  14. #include "outer.hh"
  15. #include <memory>
  16. #include <array>
  17. namespace openmsx {
  18. class PostProcessor;
  19. class Renderer;
  20. class VDPCmdEngine;
  21. class VDPVRAM;
  22. class MSXCPU;
  23. class SpriteChecker;
  24. class Display;
  25. class RawFrame;
  26. class Setting;
  27. template<typename> class EnumSetting;
  28. namespace VDPAccessSlots {
  29. enum Delta : int;
  30. class Calculator;
  31. }
  32. /** Unified implementation of MSX Video Display Processors (VDPs).
  33. * MSX1 VDP is Texas Instruments TMS9918A or TMS9928A.
  34. * MSX2 VDP is Yamaha V9938.
  35. * MSX2+ and turbo R VDP is Yamaha V9958.
  36. *
  37. * Current implementation is written by Maarten ter Huurne,
  38. * with some contributions from other openMSX developers.
  39. * The first implementation was based on Sean Young's code from MESS,
  40. * which was in turn based on code by Mike Balfour.
  41. * Integration of MESS code into openMSX was done by David Heremans.
  42. *
  43. * All undocumented features as described in the following file
  44. * should be emulated:
  45. * http://www.msxnet.org/tech/tms9918a.txt
  46. *
  47. * This class is the VDP core, it uses a separate Renderer to
  48. * convert the VRAM contents into pixels on the host machine.
  49. * Communicates with the Renderer through a pull variant of the
  50. * Observer pattern: a VDP object fires events when its state changes,
  51. * the Renderer can retrieve the new state if necessary by calling
  52. * methods on the VDP object.
  53. *
  54. * A note about timing: the start of a frame or line is defined as
  55. * the starting time of the corresponding sync (vsync, hsync).
  56. */
  57. class VDP final : public MSXDevice, private VideoSystemChangeListener
  58. , private Observer<Setting>
  59. {
  60. public:
  61. /** Number of VDP clock ticks per second.
  62. */
  63. static constexpr int TICKS_PER_SECOND = 3579545 * 6; // 21.5MHz;
  64. using VDPClock = Clock<TICKS_PER_SECOND>;
  65. /** Number of VDP clock ticks per line.
  66. */
  67. static constexpr int TICKS_PER_LINE = 1368;
  68. explicit VDP(const DeviceConfig& config);
  69. ~VDP() override;
  70. void powerUp(EmuTime::param time) override;
  71. void reset(EmuTime::param time) override;
  72. byte readIO(word port, EmuTime::param time) override;
  73. byte peekIO(word port, EmuTime::param time) const override;
  74. void writeIO(word port, byte value, EmuTime::param time) override;
  75. /** Used by Video9000 to be able to couple the VDP and V9990 output.
  76. * Can return nullptr in case of renderer=none. This value can change
  77. * over the lifetime of the VDP object (on renderer switch).
  78. */
  79. PostProcessor* getPostProcessor() const;
  80. /** Is this an MSX1 VDP?
  81. * @return True if this is an MSX1 VDP
  82. * False otherwise.
  83. */
  84. inline bool isMSX1VDP() const {
  85. return (version & VM_MSX1) != 0;
  86. }
  87. /** Is this a VDP only capable of PAL?
  88. * @return True iff this is a PAL only VDP
  89. */
  90. inline bool isVDPwithPALonly() const {
  91. return (version & VM_PAL) != 0;
  92. }
  93. /** Is this a VDP that lacks mirroring?
  94. * @return True iff this VDP lacks the screen 2 mirrored mode
  95. */
  96. inline bool vdpLacksMirroring() const {
  97. return (version & VM_NO_MIRRORING) != 0;
  98. }
  99. /** Is this a VDP that has pattern/colortable mirroring?
  100. * @return True iff this VDP has pattern/colortable mirroring
  101. */
  102. inline bool vdpHasPatColMirroring() const {
  103. return (version & VM_PALCOL_MIRRORING) != 0;
  104. }
  105. /** Does this VDP have VRAM remapping when switching from 4k to 8/16k mode?
  106. * @return True iff this is a VDP with VRAM remapping
  107. */
  108. inline bool isVDPwithVRAMremapping() const {
  109. return (version & VM_VRAM_REMAPPING) != 0;
  110. }
  111. /** Does this VDP support YJK display?
  112. * @return True for V9958, false otherwise.
  113. */
  114. inline bool hasYJK() const {
  115. return (version & VM_YJK) != 0;
  116. }
  117. /** Get the (fixed) palette for this MSX1 VDP.
  118. * Don't use this if it's not an MSX1 VDP!
  119. * @return an array of 16 RGB triplets
  120. */
  121. std::array<std::array<uint8_t, 3>, 16> getMSX1Palette() const;
  122. /** Get the display mode the VDP is in.
  123. * @return The current display mode.
  124. */
  125. inline DisplayMode getDisplayMode() const {
  126. return displayMode;
  127. }
  128. /** Get the VRAM object for this VDP.
  129. */
  130. inline VDPVRAM& getVRAM() {
  131. return *vram;
  132. }
  133. /** Are we currently superimposing?
  134. * In case of superimpose, returns a pointer to the to-be-superimposed
  135. * frame. Returns nullptr if superimpose is not active.
  136. */
  137. inline const RawFrame* isSuperimposing() const {
  138. // Note that bit 0 of r#0 has no effect on an V9938 or higher,
  139. // but this bit is masked out. Also note that on an MSX1, if
  140. // bit 0 of r#0 is enabled and there is no external video
  141. // source, then we lose sync.
  142. // Also note that because this property is fixed per frame we
  143. // cannot (re)calculate it from register values.
  144. return superimposing;
  145. }
  146. /** Get the sprite checker for this VDP.
  147. */
  148. inline SpriteChecker& getSpriteChecker() {
  149. return *spriteChecker;
  150. }
  151. /** Gets the current transparency setting.
  152. * @return True iff color 0 is transparent.
  153. */
  154. inline bool getTransparency() const {
  155. return (controlRegs[8] & 0x20) == 0;
  156. }
  157. /** Can a sprite which has color=0 collide with some other sprite?
  158. */
  159. bool canSpriteColor0Collide() const {
  160. // On MSX1 (so far only tested a TMS9129(?)) sprites with
  161. // color=0 can always collide with other sprites. Though on
  162. // V99x8 (only tested V9958) collisions only occur when color 0
  163. // is not transparent. For more details see:
  164. // https://github.com/openMSX/openMSX/issues/1198
  165. return isMSX1VDP() || !getTransparency();
  166. }
  167. /** Gets the current foreground color.
  168. * @return Color index [0..15].
  169. */
  170. inline int getForegroundColor() const {
  171. return controlRegs[7] >> 4;
  172. }
  173. /** Gets the current background color.
  174. * @return Color index.
  175. * In Graphic5 mode, the range is [0..15];
  176. * bits 3-2 contains the color for even pixels,
  177. * bits 1-0 contains the color for odd pixels.
  178. * In Graphic7 mode with YJK off, the range is [0..255].
  179. * In other modes, the range is [0..15].
  180. */
  181. inline int getBackgroundColor() const {
  182. byte reg7 = controlRegs[7];
  183. if (displayMode.getByte() == DisplayMode::GRAPHIC7) {
  184. return reg7;
  185. } else {
  186. return reg7 & 0x0F;
  187. }
  188. }
  189. /** Gets the current blinking color for blinking text.
  190. * @return Color index [0..15].
  191. */
  192. inline int getBlinkForegroundColor() const {
  193. return controlRegs[12] >> 4;
  194. }
  195. /** Gets the current blinking color for blinking text.
  196. * @return Color index [0..15].
  197. */
  198. inline int getBlinkBackgroundColor() const {
  199. return controlRegs[12] & 0x0F;
  200. }
  201. /** Gets the current blink state.
  202. * @return True iff alternate colors / page should be displayed.
  203. */
  204. inline bool getBlinkState() const {
  205. return blinkState;
  206. }
  207. /** Gets a palette entry.
  208. * @param index The index [0..15] in the palette.
  209. * @return Color value in the format of the palette registers:
  210. * bit 10..8 is green, bit 6..4 is red and bit 2..0 is blue.
  211. */
  212. inline word getPalette(int index) const {
  213. return palette[index];
  214. }
  215. /** Is the display enabled?
  216. * Both the regular border and forced blanking by clearing
  217. * the display enable bit are considered disabled display.
  218. * @return true iff enabled.
  219. */
  220. inline bool isDisplayEnabled() const {
  221. return isDisplayArea && displayEnabled;
  222. }
  223. /** Are sprites enabled?
  224. * @return True iff blanking is off, the current mode supports
  225. * sprites and sprites are not disabled.
  226. */
  227. inline bool spritesEnabled() const {
  228. return displayEnabled &&
  229. (displayMode.getSpriteMode(isMSX1VDP()) != 0) &&
  230. ((controlRegs[8] & 0x02) == 0x00);
  231. }
  232. /** Same as spritesEnabled(), but may only be called in sprite
  233. * mode 1 or 2. Is a tiny bit faster.
  234. */
  235. inline bool spritesEnabledFast() const {
  236. assert(displayMode.getSpriteMode(isMSX1VDP()) != 0);
  237. return displayEnabled && ((controlRegs[8] & 0x02) == 0x00);
  238. }
  239. /** Still faster variant (just looks at the sprite-enabled-bit).
  240. * But only valid in sprite mode 1/2 with screen enabled.
  241. */
  242. inline bool spritesEnabledRegister() const {
  243. return (controlRegs[8] & 0x02) == 0x00;
  244. }
  245. /** Gets the current vertical scroll (line displayed at Y=0).
  246. * @return Vertical scroll register value.
  247. */
  248. inline byte getVerticalScroll() const {
  249. return controlRegs[23];
  250. }
  251. /** Gets the current horizontal scroll lower bits.
  252. * Rather than actually scrolling the screen, this setting shifts the
  253. * screen 0..7 bytes to the right.
  254. * @return Horizontal scroll low register value.
  255. */
  256. inline byte getHorizontalScrollLow() const {
  257. return controlRegs[27];
  258. }
  259. /** Gets the current horizontal scroll higher bits.
  260. * This value determines how many 8-pixel steps the background is
  261. * rotated to the left.
  262. * @return Horizontal scroll high register value.
  263. */
  264. inline byte getHorizontalScrollHigh() const {
  265. return controlRegs[26];
  266. }
  267. /** Gets the current border mask setting.
  268. * Border mask extends the left border by 8 pixels if enabled.
  269. * This is a V9958 feature, on older VDPs it always returns false.
  270. * @return true iff enabled.
  271. */
  272. inline bool isBorderMasked() const {
  273. return (controlRegs[25] & 0x02) != 0;
  274. }
  275. /** Is multi page scrolling enabled?
  276. * It is considered enabled if both the multi page scrolling flag is
  277. * enabled and the current page is odd. Scrolling wraps into the
  278. * lower even page.
  279. * @return true iff enabled.
  280. */
  281. inline bool isMultiPageScrolling() const {
  282. return (controlRegs[25] & 0x01) && (controlRegs[2] & 0x20);
  283. }
  284. /** Get the absolute line number of display line zero.
  285. * Usually this is equal to the height of the top border,
  286. * but not so during overscan.
  287. */
  288. inline int getLineZero() const {
  289. return displayStart / TICKS_PER_LINE;
  290. }
  291. /** Is PAL timing active?
  292. * This setting is fixed at start of frame.
  293. * @return True if PAL timing, false if NTSC timing.
  294. */
  295. inline bool isPalTiming() const {
  296. return palTiming;
  297. }
  298. /** Get interlace status.
  299. * Interlace means the odd fields are displayed half a line lower
  300. * than the even fields. Together with even/odd page alternation
  301. * this can be used to create an interlaced display.
  302. * This setting is fixed at start of frame.
  303. * @return True iff interlace is enabled.
  304. */
  305. inline bool isInterlaced() const {
  306. return interlaced;
  307. }
  308. /** Get 'fast-blink' status.
  309. * Normally blinking timing (alternating between pages) is based on
  310. * frames. Though the V99x8 has an undocumented feature which changes
  311. * this timing to lines. Sometimes this is called the "Cadari" feature
  312. * after Luciano Cadari who discovered it.
  313. *
  314. * See ticket#1091: "Support for undocumented V99x8 register 1 bit 2"
  315. * https://github.com/openMSX/openMSX/issues/1091
  316. * for testcases and links to more information.
  317. */
  318. inline bool isFastBlinkEnabled() const {
  319. return (controlRegs[1] & 4) != 0;
  320. }
  321. /** Get even/odd page alternation status.
  322. * Interlace means the odd fields are displayed half a line lower
  323. * than the even fields. Together with even/odd page alternation
  324. * this can be used to create an interlaced display.
  325. * This setting is NOT fixed at start of frame.
  326. * TODO: Find out how real VDP does it.
  327. * If it fixes it at start of frame, so should we.
  328. * If it handles it dynamically (my guess), then an update method
  329. * should be added on the Renderer interface.
  330. * @return True iff even/odd page alternation is enabled.
  331. */
  332. inline bool isEvenOddEnabled() const {
  333. if (isFastBlinkEnabled()) return false;
  334. return (controlRegs[9] & 4) != 0;
  335. }
  336. /** Is the even or odd field being displayed?
  337. * @return True iff this field should be displayed half a line lower.
  338. */
  339. inline bool getEvenOdd() const {
  340. return (statusReg2 & 2) != 0;
  341. }
  342. /** Expresses the state of even/odd page interchange in a mask
  343. * on the line number. If even/odd interchange is active, for some
  344. * frames lines 256..511 (page 1) are replaced by 0..255 (page 0)
  345. * and 768..1023 (page 3, if applicable) by 512..767 (page 2).
  346. * Together with the interlace setting this can be used to create
  347. * an interlaced display.
  348. * Even/odd interchange can also happen because of the 'blink'
  349. * feature in bitmap modes.
  350. * @pre !isFastBlinkEnabled()
  351. * @return Line number mask that expressed even/odd state.
  352. */
  353. inline int getEvenOddMask() const {
  354. // TODO: Verify which page is displayed on even fields.
  355. assert(!isFastBlinkEnabled());
  356. return (((~controlRegs[9] & 4) << 6) | ((statusReg2 & 2) << 7)) &
  357. (!blinkState << 8);
  358. }
  359. /** Similar to the above getEvenOddMask() method, but can also be
  360. * called when 'isFastBlinkEnabled() == true'. In the latter case
  361. * the timinig is based on lines instead of frames. This means the
  362. * result is no longer fixed per frame, and thus this method takes
  363. * an additional line parameter.
  364. */
  365. inline int getEvenOddMask(int line) const {
  366. if (isFastBlinkEnabled()) {
  367. // EO and IL not considered in this mode
  368. auto p = calculateLineBlinkState(line);
  369. return (!p.first) << 8;
  370. } else {
  371. return getEvenOddMask();
  372. }
  373. }
  374. /** Calculates what 'blinkState' and 'blinkCount' would be at a specific line.
  375. * (The actual 'blinkState' and 'blinkCount' variables represent the values
  376. * for line 0 and remain fixed for the duration of the frame.
  377. */
  378. inline std::pair<bool, int> calculateLineBlinkState(unsigned line) const {
  379. assert(isFastBlinkEnabled());
  380. if (blinkCount == 0) { // not changing
  381. return {blinkState, blinkCount};
  382. }
  383. unsigned evenLen = ((controlRegs[13] >> 4) & 0x0F) * 10;
  384. unsigned oddLen = ((controlRegs[13] >> 0) & 0x0F) * 10;
  385. unsigned totalLen = evenLen + oddLen;
  386. assert(totalLen != 0); // because this implies 'blinkCount == 0'
  387. line %= totalLen; // reduce double flips
  388. bool resultState = blinkState; // initial guess, adjusted later
  389. if (blinkState) {
  390. // We start in the 'even' period -> check first for
  391. // even/odd transition, next for odd/even
  392. } else {
  393. // We start in the 'odd' period -> do the opposite
  394. std::swap(evenLen, oddLen);
  395. }
  396. int newCount = blinkCount - line;
  397. if (newCount <= 0) {
  398. // switch even->odd (or odd->even)
  399. resultState = !resultState;
  400. newCount += oddLen;
  401. if (newCount <= 0) {
  402. // switch odd->even (or even->odd)
  403. resultState = !resultState;
  404. newCount += evenLen;
  405. assert(newCount > 0);
  406. }
  407. }
  408. return {resultState, newCount};
  409. }
  410. /** Gets the number of VDP clock ticks (21MHz) elapsed between
  411. * a given time and the start of this frame.
  412. */
  413. inline int getTicksThisFrame(EmuTime::param time) const {
  414. return frameStartTime.getTicksTill_fast(time);
  415. }
  416. inline EmuTime::param getFrameStartTime() const {
  417. return frameStartTime.getTime();
  418. }
  419. /** Gets the sprite size in pixels (8/16).
  420. */
  421. inline int getSpriteSize() const {
  422. return ((controlRegs[1] & 2) << 2) + 8;
  423. }
  424. /** Are sprites magnified?
  425. */
  426. inline bool isSpriteMag() const {
  427. return controlRegs[1] & 1;
  428. }
  429. /** Are commands possible in non Graphic modes? (V9958 only)
  430. * @return True iff CMD bit set.
  431. */
  432. inline bool getCmdBit() const {
  433. return (controlRegs[25] & 0x40) != 0;
  434. }
  435. /** Gets the number of lines per frame.
  436. */
  437. inline int getLinesPerFrame() const {
  438. return palTiming ? 313 : 262;
  439. }
  440. /** Gets the number of VDP clockticks (21MHz) per frame.
  441. */
  442. inline int getTicksPerFrame() const {
  443. return getLinesPerFrame() * TICKS_PER_LINE;
  444. }
  445. /** Is the given timestamp inside the current frame?
  446. * Mainly useful for debugging, because relevant timestamps should
  447. * always be inside the current frame.
  448. * The end time of a frame is still considered inside,
  449. * to support the case when the given timestamp is exclusive itself:
  450. * a typically "limit" variable.
  451. * @param time Timestamp to check.
  452. * @return True iff the timestamp is inside the current frame.
  453. */
  454. inline bool isInsideFrame(EmuTime::param time) const {
  455. return time >= frameStartTime.getTime() &&
  456. getTicksThisFrame(time) <= getTicksPerFrame();
  457. }
  458. /** This is a combination of the (horizontal) set adjust register and
  459. * the YJK-mode bit.
  460. */
  461. inline int getHorizontalAdjust() const {
  462. return horizontalAdjust;
  463. }
  464. /** Gets the number of VDP clockticks between start of line and the start
  465. * of the sprite plane.
  466. * The location of the sprite plane is not influenced by horizontal scroll
  467. * or border mask.
  468. * TODO: Leave out the text mode case, since there are no sprites
  469. * in text mode?
  470. */
  471. inline int getLeftSprites() const {
  472. return 100 + 102 + 56
  473. + (horizontalAdjust - 7) * 4
  474. + (displayMode.isTextMode() ? 36 : 0);
  475. }
  476. /** Gets the number of VDP clockticks between start of line and the end
  477. * of the left border.
  478. * Does not include extra pixels of horizontal scroll low, since those
  479. * are not actually border pixels (sprites appear in front of them).
  480. */
  481. inline int getLeftBorder() const {
  482. return getLeftSprites() + (isBorderMasked() ? 8 * 4 : 0);
  483. }
  484. /** Gets the number of VDP clockticks between start of line and the start
  485. * of the right border.
  486. */
  487. inline int getRightBorder() const {
  488. return getLeftSprites()
  489. + (displayMode.isTextMode() ? 960 : 1024);
  490. }
  491. /** Gets the number of VDP clockticks between start of line and the time
  492. * when the background pixel with X coordinate 0 would be drawn.
  493. * This includes extra pixels of horizontal scroll low,
  494. * but disregards border mask.
  495. */
  496. inline int getLeftBackground() const {
  497. return getLeftSprites() + getHorizontalScrollLow() * 4;
  498. }
  499. /** Should only be used by SpriteChecker. Returns the current value
  500. * of status register 0 (both the F-flag and the sprite related bits).
  501. */
  502. byte getStatusReg0() const { return statusReg0; }
  503. /** Should only be used by SpriteChecker. Change the sprite related
  504. * bits of status register 0 (leaves the F-flag unchanged).
  505. * Bit 6 (5S) is set when more than 4 (sprite mode 1) or 8 (sprite
  506. * mode 2) sprites occur on the same line.
  507. * Bit 5 (C) is set when sprites collide.
  508. * Bit 4..0 (5th sprite number) contains the number of the first
  509. * sprite to exceed the limit per line.
  510. */
  511. void setSpriteStatus(byte value)
  512. {
  513. statusReg0 = (statusReg0 & 0x80) | (value & 0x7F);
  514. }
  515. /** Returns current VR mode.
  516. * false -> VR=0, true -> VR=1
  517. */
  518. bool getVRMode() const {
  519. return (controlRegs[8] & 8) != 0;
  520. }
  521. /** Enable superimposing
  522. */
  523. void setExternalVideoSource(const RawFrame* externalSource) {
  524. externalVideo = externalSource;
  525. }
  526. /** Value of the cmdTiming setting, true means commands have infinite speed.
  527. */
  528. bool getBrokenCmdTiming() const {
  529. return brokenCmdTiming;
  530. }
  531. /** Get the earliest access slot that is at least 'delta' cycles in
  532. * the future. */
  533. EmuTime getAccessSlot(EmuTime::param time, VDPAccessSlots::Delta delta) const;
  534. /** Same as getAccessSlot(), but it can be _much_ faster for repeated
  535. * calls, e.g. in the implementation of VDP commands. However it does
  536. * have some limitations:
  537. * - The returned calculator only remains valid for as long as
  538. * the VDP access timing remains the same (display/sprite enable).
  539. * - The calculator needs to see _all_ time changes.
  540. * (So this means that in every VDPCmd::execute() method you need
  541. * to construct a new calculator).
  542. */
  543. VDPAccessSlots::Calculator getAccessSlotCalculator(
  544. EmuTime::param time, EmuTime::param limit) const;
  545. /** Only used when there are commandExecuting-probe listeners.
  546. *
  547. * Call to announce a (lower-bound) estimate for when the VDP command
  548. * will finish executing. In response the VDP will schedule a
  549. * synchronization point to sync with VDPCmdEngine emulation.
  550. *
  551. * Normally it's not required to pro-actively sync with the end of a
  552. * VDP command. Instead these sync happen reactively on VDP status
  553. * reads (e.g. polling the CE bit) or on VRAM reads (rendering or CPU
  554. * VRAM read). This is in contrast with the V9990 where we DO need an
  555. * active sync because the V9990 can generate an IRQ on command end.
  556. *
  557. * Though when the VDP.commandExecuting probe is in use we do want a
  558. * reasonably timing accurate reaction of that probe. So (only) then we
  559. * do add the extra syncs (thus with extra emulation overhead when you
  560. * use that probe).
  561. */
  562. void scheduleCmdSync(EmuTime t) {
  563. auto now = getCurrentTime();
  564. if (t <= now) {
  565. // The largest amount of VDP cycles between 'progress'
  566. // in command emulation:
  567. // - worst case the LMMM takes 120+64 cycles to fully process one pixel
  568. // - the largest gap between access slots is 70 cycles
  569. // - but if we're unlucky the CPU steals that slot
  570. int LARGEST_STALL = 184 + 2 * 70;
  571. t = now + VDPClock::duration(LARGEST_STALL);
  572. }
  573. syncCmdDone.setSyncPoint(t);
  574. }
  575. template<typename Archive>
  576. void serialize(Archive& ar, unsigned version);
  577. private:
  578. void initTables();
  579. // VdpVersion bitmasks
  580. static constexpr unsigned VM_MSX1 = 1; // set-> MSX1, unset-> MSX2 or MSX2+
  581. static constexpr unsigned VM_PAL = 2; // set-> fixed PAL, unset-> fixed NTSC or switchable
  582. static constexpr unsigned VM_NO_MIRRORING = 4; // set-> no (screen2) mirroring
  583. static constexpr unsigned VM_PALCOL_MIRRORING = 8; // set-> pattern/color-table mirroring
  584. static constexpr unsigned VM_VRAM_REMAPPING = 16; // set-> 4k,8/16k VRAM remapping
  585. static constexpr unsigned VM_TOSHIBA_PALETTE = 32; // set-> has Toshiba palette
  586. static constexpr unsigned VM_YJK = 64; // set-> has YJK (MSX2+)
  587. static constexpr unsigned VM_YM2220_PALETTE = 128; // set-> has YM2220 palette
  588. /** VDP version: the VDP model being emulated. */
  589. enum VdpVersion {
  590. /** MSX1 VDP, NTSC version.
  591. * TMS9918A has NTSC encoding built in,
  592. * while TMS9928A has color difference output;
  593. * in emulation there is no difference. */
  594. TMS99X8A = VM_MSX1 | VM_PALCOL_MIRRORING | VM_VRAM_REMAPPING,
  595. /** MSX1 VDP, PAL version. */
  596. TMS9929A = VM_MSX1 | VM_PALCOL_MIRRORING | VM_VRAM_REMAPPING | VM_PAL,
  597. /** newer variant PAL. */
  598. TMS9129 = VM_MSX1 | VM_PAL,
  599. /** newer variant NTSC. */
  600. TMS91X8 = VM_MSX1,
  601. /** Toshiba clone (hardwired as PAL). */
  602. T6950PAL = VM_MSX1 | VM_TOSHIBA_PALETTE | VM_NO_MIRRORING | VM_PAL,
  603. /** Toshiba clone (hardwired as NTSC). */
  604. T6950NTSC = VM_MSX1 | VM_TOSHIBA_PALETTE | VM_NO_MIRRORING,
  605. /** VDP in Toshiba T7937A engine (hardwired as PAL). */
  606. T7937APAL = VM_MSX1 | VM_TOSHIBA_PALETTE | VM_PAL,
  607. /** VDP in Toshiba T7937A engine (hardwired as NTSC). */
  608. T7937ANTSC = VM_MSX1 | VM_TOSHIBA_PALETTE,
  609. /** Yamaha clone (hardwired as PAL). */
  610. YM2220PAL = VM_MSX1 | VM_YM2220_PALETTE | VM_PALCOL_MIRRORING | VM_PAL,
  611. /** Yamaha clone (hardwired as NTSC). */
  612. YM2220NTSC = VM_MSX1 | VM_YM2220_PALETTE | VM_PALCOL_MIRRORING,
  613. /** MSX2 VDP. */
  614. V9938 = 0,
  615. /** MSX2+ and turbo R VDP. */
  616. V9958 = VM_YJK,
  617. };
  618. struct SyncBase : public Schedulable {
  619. explicit SyncBase(VDP& vdp_) : Schedulable(vdp_.getScheduler()) {}
  620. using Schedulable::removeSyncPoint;
  621. using Schedulable::setSyncPoint;
  622. using Schedulable::pendingSyncPoint;
  623. protected:
  624. ~SyncBase() = default;
  625. };
  626. struct SyncVSync final : public SyncBase {
  627. explicit SyncVSync(VDP& vdp) : SyncBase(vdp) {}
  628. void executeUntil(EmuTime::param time) override {
  629. auto& vdp = OUTER(VDP, syncVSync);
  630. vdp.execVSync(time);
  631. }
  632. } syncVSync;
  633. struct SyncDisplayStart final : public SyncBase {
  634. explicit SyncDisplayStart(VDP& vdp) : SyncBase(vdp) {}
  635. void executeUntil(EmuTime::param time) override {
  636. auto& vdp = OUTER(VDP, syncDisplayStart);
  637. vdp.execDisplayStart(time);
  638. }
  639. } syncDisplayStart;
  640. struct SyncVScan final : public SyncBase {
  641. explicit SyncVScan(VDP& vdp) : SyncBase(vdp) {}
  642. void executeUntil(EmuTime::param time) override {
  643. auto& vdp = OUTER(VDP, syncVScan);
  644. vdp.execVScan(time);
  645. }
  646. } syncVScan;
  647. struct SyncHScan final : public SyncBase {
  648. explicit SyncHScan(VDP& vdp) : SyncBase(vdp) {}
  649. void executeUntil(EmuTime::param /*time*/) override {
  650. auto& vdp = OUTER(VDP, syncHScan);
  651. vdp.execHScan();
  652. }
  653. } syncHScan;
  654. struct SyncHorAdjust final : public SyncBase {
  655. explicit SyncHorAdjust(VDP& vdp) : SyncBase(vdp) {}
  656. void executeUntil(EmuTime::param time) override {
  657. auto& vdp = OUTER(VDP, syncHorAdjust);
  658. vdp.execHorAdjust(time);
  659. }
  660. } syncHorAdjust;
  661. struct SyncSetMode final : public SyncBase {
  662. explicit SyncSetMode(VDP& vdp) : SyncBase(vdp) {}
  663. void executeUntil(EmuTime::param time) override {
  664. auto& vdp = OUTER(VDP, syncSetMode);
  665. vdp.execSetMode(time);
  666. }
  667. } syncSetMode;
  668. struct SyncSetBlank final : public SyncBase {
  669. explicit SyncSetBlank(VDP& vdp) : SyncBase(vdp) {}
  670. void executeUntil(EmuTime::param time) override {
  671. auto& vdp = OUTER(VDP, syncSetBlank);
  672. vdp.execSetBlank(time);
  673. }
  674. } syncSetBlank;
  675. struct SyncCpuVramAccess final : public SyncBase {
  676. explicit SyncCpuVramAccess(VDP& vdp) : SyncBase(vdp) {}
  677. void executeUntil(EmuTime::param time) override {
  678. auto& vdp = OUTER(VDP, syncCpuVramAccess);
  679. vdp.execCpuVramAccess(time);
  680. }
  681. } syncCpuVramAccess;
  682. struct SyncCmdDone final : public SyncBase {
  683. explicit SyncCmdDone(VDP& vdp) : SyncBase(vdp) {}
  684. void executeUntil(EmuTime::param time) override {
  685. auto& vdp = OUTER(VDP, syncCmdDone);
  686. vdp.execSyncCmdDone(time);
  687. }
  688. } syncCmdDone;
  689. void execVSync(EmuTime::param time);
  690. void execDisplayStart(EmuTime::param time);
  691. void execVScan(EmuTime::param time);
  692. void execHScan();
  693. void execHorAdjust(EmuTime::param time);
  694. void execSetMode(EmuTime::param time);
  695. void execSetBlank(EmuTime::param time);
  696. void execCpuVramAccess(EmuTime::param time);
  697. void execSyncCmdDone(EmuTime::param time);
  698. /** Gets the number of display lines per screen.
  699. * @return 192 or 212.
  700. */
  701. inline int getNumberOfLines() const {
  702. return controlRegs[9] & 0x80 ? 212 : 192;
  703. }
  704. /** Returns the amount of vertical set-adjust 0..15.
  705. * Neutral set-adjust (that is 'set adjust(0,0)') returns the value '7'.
  706. */
  707. int getVerticalAdjust() const {
  708. return (controlRegs[18] >> 4) ^ 0x07;
  709. }
  710. /** Gets the value of the horizontal retrace status bit.
  711. * Note that HR flipping continues at all times, not just during
  712. * vertical display range.
  713. * @param ticksThisFrame The screen position (in VDP ticks)
  714. * to return HR for.
  715. * @return True iff the VDP scanning is inside the left/right
  716. * border or left/right erase or horizontal sync.
  717. * False iff the VDP scanning is in the display range.
  718. */
  719. inline bool getHR(int ticksThisFrame) const {
  720. // Note: These constants are located inside this function because
  721. // GCC 4.0.x won't link if they are in the class scope.
  722. /** Length of horizontal blank (HR=1) in text mode, measured in VDP
  723. * ticks.
  724. */
  725. static constexpr int HBLANK_LEN_TXT = 404;
  726. /** Length of horizontal blank (HR=1) in graphics mode, measured in VDP
  727. * ticks.
  728. */
  729. static constexpr int HBLANK_LEN_GFX = 312;
  730. return
  731. ( ticksThisFrame + TICKS_PER_LINE - getRightBorder()
  732. ) % TICKS_PER_LINE
  733. < (displayMode.isTextMode() ? HBLANK_LEN_TXT : HBLANK_LEN_GFX);
  734. }
  735. // VideoSystemChangeListener interface:
  736. void preVideoSystemChange() override;
  737. void postVideoSystemChange() override;
  738. /** Called both on init and on reset.
  739. * Puts VDP into reset state.
  740. * Does not call any renderer methods.
  741. */
  742. void resetInit();
  743. /** Companion to resetInit: in resetInit the registers are reset,
  744. * in this method the new base masks are distributed to the VDP
  745. * subsystems.
  746. */
  747. void resetMasks(EmuTime::param time);
  748. /** Start a new frame.
  749. * @param time The moment in emulated time the frame starts.
  750. */
  751. void frameStart(EmuTime::param time);
  752. /** Schedules a DISPLAY_START sync point.
  753. * Also removes a pending DISPLAY_START sync, if any.
  754. * Since HSCAN and VSCAN are relative to display start,
  755. * their schedule methods are called by this method.
  756. * @param time The moment in emulated time this call takes place.
  757. * Note: time is not the DISPLAY_START sync time!
  758. */
  759. void scheduleDisplayStart(EmuTime::param time);
  760. /** Schedules a VSCAN sync point.
  761. * Also removes a pending VSCAN sync, if any.
  762. * @param time The moment in emulated time this call takes place.
  763. * Note: time is not the VSCAN sync time!
  764. */
  765. void scheduleVScan(EmuTime::param time);
  766. /** Schedules a HSCAN sync point.
  767. * Also removes a pending HSCAN sync, if any.
  768. * @param time The moment in emulated time this call takes place.
  769. * Note: time is not the HSCAN sync time!
  770. */
  771. void scheduleHScan(EmuTime::param time);
  772. /** Byte is written to VRAM by the CPU.
  773. */
  774. void vramWrite(byte value, EmuTime::param time);
  775. /** Byte is read from VRAM by the CPU.
  776. */
  777. byte vramRead(EmuTime::param time);
  778. /** Helper methods for CPU-VRAM access. */
  779. void scheduleCpuVramAccess(bool isRead, byte write, EmuTime::param time);
  780. void executeCpuVramAccess(EmuTime::param time);
  781. /** Read the contents of a status register
  782. */
  783. byte peekStatusReg(byte reg, EmuTime::param time) const;
  784. byte readStatusReg(byte reg, EmuTime::param time);
  785. /** VDP control register has changed, work out the consequences.
  786. */
  787. void changeRegister(byte reg, byte val, EmuTime::param time);
  788. /** Schedule a sync point at the start of the next line.
  789. */
  790. void syncAtNextLine(SyncBase& type, EmuTime::param time);
  791. /** Create a new renderer.
  792. */
  793. void createRenderer();
  794. /** Name base mask has changed.
  795. * Inform the renderer and the VRAM.
  796. */
  797. void updateNameBase(EmuTime::param time);
  798. /** Color base mask has changed.
  799. * Inform the renderer and the VRAM.
  800. */
  801. void updateColorBase(EmuTime::param time);
  802. /** Pattern base mask has changed.
  803. * Inform the renderer and the VRAM.
  804. */
  805. void updatePatternBase(EmuTime::param time);
  806. /** Sprite attribute base mask has changed.
  807. * Inform the SpriteChecker and the VRAM.
  808. */
  809. void updateSpriteAttributeBase(EmuTime::param time);
  810. /** Sprite pattern base mask has changed.
  811. * Inform the SpriteChecker and the VRAM.
  812. */
  813. void updateSpritePatternBase(EmuTime::param time);
  814. /** Display mode has changed.
  815. * Update displayMode's value and inform the Renderer.
  816. */
  817. void updateDisplayMode(DisplayMode newMode, bool cmdBit, EmuTime::param time);
  818. /** Sets a palette entry.
  819. * @param index The index [0..15] in the palette.
  820. * @param grb value in the format of the palette registers:
  821. * bit 10..8 is green, bit 6..4 is red and bit 2..0 is blue.
  822. * @param time Moment in time palette change occurs.
  823. */
  824. void setPalette(int index, word grb, EmuTime::param time);
  825. // Observer<Setting>
  826. void update(const Setting& setting) override;
  827. private:
  828. Display& display;
  829. EnumSetting<bool>& cmdTiming;
  830. EnumSetting<bool>& tooFastAccess;
  831. struct RegDebug final : SimpleDebuggable {
  832. explicit RegDebug(VDP& vdp);
  833. byte read(unsigned address) override;
  834. void write(unsigned address, byte value, EmuTime::param time) override;
  835. } vdpRegDebug;
  836. struct StatusRegDebug final : SimpleDebuggable {
  837. explicit StatusRegDebug(VDP& vdp);
  838. byte read(unsigned address, EmuTime::param time) override;
  839. } vdpStatusRegDebug;
  840. struct PaletteDebug final : SimpleDebuggable {
  841. explicit PaletteDebug(VDP& vdp);
  842. byte read(unsigned address) override;
  843. void write(unsigned address, byte value, EmuTime::param time) override;
  844. } vdpPaletteDebug;
  845. struct VRAMPointerDebug final : SimpleDebuggable {
  846. explicit VRAMPointerDebug(VDP& vdp);
  847. byte read(unsigned address) override;
  848. void write(unsigned address, byte value, EmuTime::param time) override;
  849. } vramPointerDebug;
  850. class Info : public InfoTopic {
  851. public:
  852. void execute(span<const TclObject> tokens,
  853. TclObject& result) const override;
  854. std::string help(const std::vector<std::string>& tokens) const override;
  855. virtual int calc(const EmuTime& time) const = 0;
  856. protected:
  857. Info(VDP& vdp_, const std::string& name, std::string helpText_);
  858. ~Info() = default;
  859. VDP& vdp;
  860. const std::string helpText;
  861. };
  862. struct FrameCountInfo final : Info {
  863. explicit FrameCountInfo(VDP& vdp);
  864. int calc(const EmuTime& time) const override;
  865. } frameCountInfo;
  866. struct CycleInFrameInfo final : Info {
  867. explicit CycleInFrameInfo(VDP& vdp);
  868. int calc(const EmuTime& time) const override;
  869. } cycleInFrameInfo;
  870. struct LineInFrameInfo final : Info {
  871. explicit LineInFrameInfo(VDP& vdp);
  872. int calc(const EmuTime& time) const override;
  873. } lineInFrameInfo;
  874. struct CycleInLineInfo final : Info {
  875. explicit CycleInLineInfo(VDP& vdp);
  876. int calc(const EmuTime& time) const override;
  877. } cycleInLineInfo;
  878. struct MsxYPosInfo final : Info {
  879. explicit MsxYPosInfo(VDP& vdp);
  880. int calc(const EmuTime& time) const override;
  881. } msxYPosInfo;
  882. struct MsxX256PosInfo final : Info {
  883. explicit MsxX256PosInfo(VDP& vdp);
  884. int calc(const EmuTime& time) const override;
  885. } msxX256PosInfo;
  886. struct MsxX512PosInfo final : Info {
  887. explicit MsxX512PosInfo(VDP& vdp);
  888. int calc(const EmuTime& time) const override;
  889. } msxX512PosInfo;
  890. /** Renderer that converts this VDP's state into an image.
  891. */
  892. std::unique_ptr<Renderer> renderer;
  893. /** Command engine: the part of the V9938/58 that executes commands.
  894. */
  895. std::unique_ptr<VDPCmdEngine> cmdEngine;
  896. /** Sprite checker: calculates sprite patterns and collisions.
  897. */
  898. std::unique_ptr<SpriteChecker> spriteChecker;
  899. /** VRAM management object.
  900. */
  901. std::unique_ptr<VDPVRAM> vram;
  902. /** Is there an external video source which we must superimpose
  903. * upon?
  904. */
  905. const RawFrame* externalVideo;
  906. /** Are we currently superimposing?
  907. * This is a combination of the 'externalVideo' member (see above) and
  908. * the superimpose-enable bit in VDP register R#0. This property only
  909. * changes at most once per frame (at the beginning of the frame).
  910. */
  911. const RawFrame* superimposing;
  912. /** The emulation time when this frame was started (vsync).
  913. */
  914. VDPClock frameStartTime;
  915. /** Manages vertical scanning interrupt request.
  916. */
  917. OptionalIRQHelper irqVertical;
  918. /** Manages horizontal scanning interrupt request.
  919. */
  920. OptionalIRQHelper irqHorizontal;
  921. /** Time of last set DISPLAY_START sync point.
  922. */
  923. EmuTime displayStartSyncTime;
  924. /** Time of last set VSCAN sync point.
  925. */
  926. EmuTime vScanSyncTime;
  927. /** Time of last set HSCAN sync point.
  928. */
  929. EmuTime hScanSyncTime;
  930. TclCallback tooFastCallback;
  931. /** VDP version.
  932. */
  933. VdpVersion version;
  934. /** Saturation of Pr component of TMS9XXXA output circuitry.
  935. * The output of the VDP and the circuitry between the output and the
  936. * output connector influences this value. Percentage in range [0:100]
  937. */
  938. int saturationPr;
  939. /** Saturation of Pb component of TMS9XXXA output circuitry.
  940. * The output of the VDP and the circuitry between the output and the
  941. * output connector influences this value. Percentage in range [0:100]
  942. */
  943. int saturationPb;
  944. /** The number of already fully rendered frames.
  945. * Not used for actual emulation, only for 'frame_count' info topic.
  946. * This value cannot be calculated from EmuTime because framerate is
  947. * not constant (PAL/NTSC).
  948. */
  949. int frameCount;
  950. /** VDP ticks between start of frame and start of display.
  951. */
  952. int displayStart;
  953. /** VDP ticks between start of frame and the moment horizontal
  954. * scan match occurs.
  955. */
  956. int horizontalScanOffset;
  957. /** Horizontal display adjust.
  958. * This value is update at the start of a line.
  959. */
  960. int horizontalAdjust;
  961. /** Control registers.
  962. */
  963. byte controlRegs[32];
  964. /** Mask on the control register index:
  965. * makes MSX2 registers inaccessible on MSX1,
  966. * instead the MSX1 registers are mirrored.
  967. */
  968. int controlRegMask;
  969. /** Mask on the values of control registers.
  970. * This saves a lot of masking when using the register values,
  971. * because it is guaranteed non-existant bits are always zero.
  972. * It also disables access to VDP features on a VDP model
  973. * which does not support those features.
  974. */
  975. byte controlValueMasks[32];
  976. /** Blinking count: number of frames until next state.
  977. * If the ON or OFF period is 0, blinkCount is fixed to 0.
  978. */
  979. int blinkCount;
  980. /** VRAM read/write access pointer.
  981. * Contains the lower 14 bits of the current VRAM access address.
  982. */
  983. int vramPointer;
  984. /** V9938 palette.
  985. */
  986. word palette[16];
  987. /** Is the current scan position inside the display area?
  988. */
  989. bool isDisplayArea;
  990. /** Is PAL timing active? False means NTSC timing.
  991. * This value is updated at the start of every frame,
  992. * to avoid problems with mid-screen PAL/NTSC switching.
  993. * @see isPalTiming.
  994. */
  995. bool palTiming;
  996. /** Is interlace active?
  997. * @see isInterlaced.
  998. */
  999. bool interlaced;
  1000. /** Status register 0.
  1001. * Both the F flag (bit 7) and the sprite related bits (bits 6-0)
  1002. * are stored here.
  1003. */
  1004. byte statusReg0;
  1005. /** Status register 1.
  1006. * Bit 7 and 6 are always zero because light pen is not implemented.
  1007. * Bit 0 is always zero; its calculation depends on IE1.
  1008. * So all that remains is the version number.
  1009. */
  1010. byte statusReg1;
  1011. /** Status register 2.
  1012. * Bit 7, 4 and 0 of this field are always zero,
  1013. * their value can be retrieved from the command engine.
  1014. */
  1015. byte statusReg2;
  1016. /** Blinking state: should alternate color / page be displayed?
  1017. */
  1018. bool blinkState;
  1019. /** First byte written through port #99, #9A or #9B.
  1020. */
  1021. byte dataLatch;
  1022. /** Does the data latch have register data (port #99) stored?
  1023. */
  1024. bool registerDataStored;
  1025. /** Does the data latch have palette data (port #9A) stored?
  1026. */
  1027. bool paletteDataStored;
  1028. /** VRAM is read as soon as VRAM pointer changes.
  1029. * TODO: Is this actually what happens?
  1030. * On TMS9928A the VRAM interface is the only access method.
  1031. * But on V9938/58 there are other ways to access VRAM;
  1032. * I wonder if they are consistent with this implementation.
  1033. * This also holds the soon-to-be-written data for CPU-VRAM writes.
  1034. */
  1035. byte cpuVramData;
  1036. /** CPU-VRAM requests are not executed immediately (though soon). This
  1037. * variable indicates whether the pending request is read or write.
  1038. */
  1039. bool cpuVramReqIsRead;
  1040. bool pendingCpuAccess; // always equal to pendingSyncPoint(CPU_VRAM_ACCESS)
  1041. /** Does CPU interface access main VRAM (false) or extended VRAM (true)?
  1042. * This is determined by MXC (R#45, bit 6).
  1043. */
  1044. bool cpuExtendedVram;
  1045. /** Current dispay mode. Note that this is not always the same as the
  1046. * display mode that can be obtained by combining the different mode
  1047. * bits because a mode change only takes place at the start of the
  1048. * next line.
  1049. */
  1050. DisplayMode displayMode;
  1051. /** Is display enabled. Note that this is not always the same as bit 6
  1052. * of R#1 because the display enable status change only takes place at
  1053. * the start of the next line.
  1054. */
  1055. bool displayEnabled;
  1056. /** Has a warning been printed.
  1057. * This is set when a warning about setting the dotclock direction
  1058. * is printed. */
  1059. bool warningPrinted;
  1060. /** Cached version of cmdTiming/tooFastAccess setting. */
  1061. bool brokenCmdTiming;
  1062. bool allowTooFastAccess;
  1063. /** Cached CPU reference */
  1064. MSXCPU& cpu;
  1065. const byte fixedVDPIOdelayCycles;
  1066. };
  1067. SERIALIZE_CLASS_VERSION(VDP, 8);
  1068. } // namespace openmsx
  1069. #endif