openMSX
Y8950Adpcm.cc
Go to the documentation of this file.
1 // The actual sample playing part is duplicated for the 'emu' domain and the
2 // 'audio' domain. The emu part is responsible for cycle accurate sample
3 // readback (see peekReg() register 0x13 and 0x14) and for cycle accurate
4 // status register updates (the status bits related to playback, e.g.
5 // end-of-sample). The audio part is responsible for the actual sound
6 // generation. This split up allows for the two parts to be out-of-sync. So for
7 // example when emulation is running faster or slower than 100% realtime speed,
8 // we both get cycle accurate emulation behaviour and still sound generation at
9 // 100% realtime speed (which is most of the time better for sound quality).
10 
11 #include "Y8950Adpcm.hh"
12 #include "Y8950.hh"
13 #include "Clock.hh"
14 #include "DeviceConfig.hh"
15 #include "MSXMotherBoard.hh"
16 #include "Math.hh"
17 #include "serialize.hh"
18 #include <algorithm>
19 
20 namespace openmsx {
21 
22 // Bitmask for register 0x07
23 constexpr int R07_RESET = 0x01;
24 constexpr int R07_SP_OFF = 0x08;
25 constexpr int R07_REPEAT = 0x10;
26 constexpr int R07_MEMORY_DATA = 0x20;
27 constexpr int R07_REC = 0x40;
28 constexpr int R07_START = 0x80;
29 constexpr int R07_MODE = 0xE0;
30 
31 // Bitmask for register 0x08
32 constexpr int R08_ROM = 0x01;
33 constexpr int R08_64K = 0x02;
34 constexpr int R08_DA_AD = 0x04;
35 constexpr int R08_SAMPL = 0x08;
36 constexpr int R08_NOTE_SET = 0x40;
37 constexpr int R08_CSM = 0x80;
38 
39 constexpr int DMAX = 0x6000;
40 constexpr int DMIN = 0x7F;
41 constexpr int DDEF = 0x7F;
42 
43 constexpr int STEP_BITS = 16;
44 constexpr int STEP_MASK = (1 << STEP_BITS) -1;
45 
46 
47 Y8950Adpcm::Y8950Adpcm(Y8950& y8950_, const DeviceConfig& config,
48  const std::string& name, unsigned sampleRam)
49  : Schedulable(config.getScheduler())
50  , y8950(y8950_)
51  , ram(config, name + " RAM", "Y8950 sample RAM", sampleRam)
52  , clock(config.getMotherBoard().getCurrentTime())
53  , volume(0)
54 {
55  clearRam();
56 }
57 
59 {
60  ram.clear(0xFF);
61 }
62 
63 void Y8950Adpcm::reset(EmuTime::param time)
64 {
66 
67  clock.reset(time);
68 
69  startAddr = 0;
70  stopAddr = 7;
71  delta = 0;
72  addrMask = (1 << 18) - 1;
73  reg7 = 0;
74  reg15 = 0;
75  readDelay = 0;
76  romBank = false;
77  writeReg(0x12, 255, time); // volume
78 
79  restart(emu);
80  restart(aud);
81 
83 }
84 
85 bool Y8950Adpcm::isPlaying() const
86 {
87  return (reg7 & 0xC0) == 0x80;
88 }
89 bool Y8950Adpcm::isMuted() const
90 {
91  return !isPlaying() || (reg7 & R07_SP_OFF);
92 }
93 
94 void Y8950Adpcm::restart(PlayData& pd)
95 {
96  pd.memPntr = startAddr;
97  pd.nowStep = (1 << STEP_BITS) - delta;
98  pd.out = 0;
99  pd.output = 0;
100  pd.diff = DDEF;
101  pd.nextLeveling = 0;
102  pd.sampleStep = 0;
103  pd.adpcm_data = 0; // dummy, avoid UMR in serialize
104 }
105 
106 void Y8950Adpcm::sync(EmuTime::param time)
107 {
108  if (isPlaying()) { // optimization, also correct without this test
109  unsigned ticks = clock.getTicksTill(time);
110  for (unsigned i = 0; isPlaying() && (i < ticks); ++i) {
111  calcSample(true); // ignore result
112  }
113  }
114  clock.advance(time);
115 }
116 
117 void Y8950Adpcm::schedule()
118 {
119  assert(isPlaying());
120  if ((stopAddr > startAddr) && (delta != 0)) {
121  // TODO possible optimization, no need to set sync points if
122  // the corresponding bit is masked in the interupt enable
123  // register
124  if (reg7 & R07_MEMORY_DATA) {
125  // we already did a sync(time), so clock is up-to-date
127  uint64_t samples = stopAddr - emu.memPntr + 1;
128  uint64_t length = (samples << STEP_BITS) +
129  ((1 << STEP_BITS) - emu.nowStep) +
130  (delta - 1);
131  stop += unsigned(length / delta);
132  setSyncPoint(stop.getTime());
133  } else {
134  // TODO we should also set a syncpoint in this case
135  // because this mode sets the STATUS_BUF_RDY bit
136  // which also triggers an IRQ
137  }
138  }
139 }
140 
141 void Y8950Adpcm::executeUntil(EmuTime::param time)
142 {
143  assert(isPlaying());
144  sync(time); // should set STATUS_EOS
145  assert(y8950.peekRawStatus() & Y8950::STATUS_EOS);
146  if (isPlaying() && (reg7 & R07_REPEAT)) {
147  schedule();
148  }
149 }
150 
151 void Y8950Adpcm::writeReg(byte rg, byte data, EmuTime::param time)
152 {
153  sync(time); // TODO only when needed
154  switch (rg) {
155  case 0x07: // START/REC/MEM DATA/REPEAT/SP-OFF/-/-/RESET
156  reg7 = data;
157  if (reg7 & R07_START) {
159  } else {
161  }
162  if (reg7 & R07_RESET) {
163  reg7 = 0;
164  }
165  if (reg7 & R07_START) {
166  // start ADPCM
167  restart(emu);
168  restart(aud);
169  }
170  if (reg7 & R07_MEMORY_DATA) {
171  // access external memory?
172  emu.memPntr = startAddr;
173  aud.memPntr = startAddr;
174  readDelay = 2; // two dummy reads
175  if ((reg7 & 0xA0) == 0x20) {
176  // Memory read or write
178  }
179  } else {
180  // access via CPU
181  emu.memPntr = 0;
182  aud.memPntr = 0;
183  }
184  removeSyncPoint();
185  if (isPlaying()) {
186  schedule();
187  }
188  break;
189 
190  case 0x08: // CSM/KEY BOARD SPLIT/-/-/SAMPLE/DA AD/64K/ROM
191  romBank = data & R08_ROM;
192  addrMask = data & R08_64K ? (1 << 16) - 1 : (1 << 18) - 1;
193  break;
194 
195  case 0x09: // START ADDRESS (L)
196  startAddr = (startAddr & 0x7F807) | (data << 3);
197  break;
198  case 0x0A: // START ADDRESS (H)
199  startAddr = (startAddr & 0x007FF) | (data << 11);
200  break;
201 
202  case 0x0B: // STOP ADDRESS (L)
203  stopAddr = (stopAddr & 0x7F807) | (data << 3);
204  if (isPlaying()) {
205  removeSyncPoint();
206  schedule();
207  }
208  break;
209  case 0x0C: // STOP ADDRESS (H)
210  stopAddr = (stopAddr & 0x007FF) | (data << 11);
211  if (isPlaying()) {
212  removeSyncPoint();
213  schedule();
214  }
215  break;
216 
217  case 0x0F: // ADPCM-DATA
218  writeData(data);
219  break;
220 
221  case 0x10: // DELTA-N (L)
222  delta = (delta & 0xFF00) | data;
223  volumeWStep = (volume * delta) >> STEP_BITS;
224  if (isPlaying()) {
225  removeSyncPoint();
226  schedule();
227  }
228  break;
229  case 0x11: // DELTA-N (H)
230  delta = (delta & 0x00FF) | (data << 8);
231  volumeWStep = (volume * delta) >> STEP_BITS;
232  if (isPlaying()) {
233  removeSyncPoint();
234  schedule();
235  }
236  break;
237 
238  case 0x12: { // ENVELOP CONTROL
239  volume = data;
240  volumeWStep = (volume * delta) >> STEP_BITS;
241  break;
242  }
243  case 0x0D: // PRESCALE (L)
244  case 0x0E: // PRESCALE (H)
245  case 0x15: // DAC-DATA (bit9-2)
246  case 0x16: // (bit1-0)
247  case 0x17: // (exponent)
248  case 0x1A: // PCM-DATA
249  // not implemented
250  break;
251  }
252 }
253 
254 void Y8950Adpcm::writeData(byte data)
255 {
256  reg15 = data;
257  if ((reg7 & R07_MODE) == 0x60) {
258  // external memory write
259  assert(!isPlaying()); // no need to update the 'aud' data
260  if (readDelay) {
261  emu.memPntr = startAddr;
262  readDelay = 0;
263  }
264  if (emu.memPntr <= stopAddr) {
265  writeMemory(emu.memPntr, data);
266  emu.memPntr += 2; // two nibbles at a time
267 
268  // reset BRDY bit in status register,
269  // which means we are processing the write
271 
272  // setup a timer that will callback us in 10
273  // master clock cycles for Y8950. In the
274  // callback set the BRDY flag to 1, which
275  // means we have written the data. For now, we
276  // don't really do this; we simply reset and
277  // set the flag in zero time, so that the IRQ
278  // will work.
280 
281  if (emu.memPntr > stopAddr) {
282  // we just received the last byte: set EOS
283  // verified on real HW:
284  // in case of EOS, BUF_RDY is set as well
286  // Eugeny tested that pointer wraps when
287  // continue writing after EOS
288  emu.memPntr = startAddr;
289  }
290  }
291 
292  } else if ((reg7 & R07_MODE) == 0x80) {
293  // ADPCM synthesis from CPU
294 
295  // Reset BRDY bit in status register, which means we
296  // are full of data
298  }
299 }
300 
301 byte Y8950Adpcm::readReg(byte rg, EmuTime::param time)
302 {
303  sync(time); // TODO only when needed
304  byte result = (rg == 0x0F)
305  ? readData() // ADPCM-DATA
306  : peekReg(rg); // other
307  return result;
308 }
309 
310 byte Y8950Adpcm::peekReg(byte rg, EmuTime::param time) const
311 {
312  const_cast<Y8950Adpcm*>(this)->sync(time); // TODO only when needed
313  return peekReg(rg);
314 }
315 
316 byte Y8950Adpcm::peekReg(byte rg) const
317 {
318  switch (rg) {
319  case 0x0F: // ADPCM-DATA
320  return peekData();
321  case 0x13:
322  // TODO check: is this before or after
323  // volume is applied
324  // filtering is performed
325  return (emu.output >> 8) & 0xFF;
326  case 0x14:
327  return emu.output >> 16;
328  default:
329  return 255;
330  }
331 }
332 
334 {
335  // If the BUF_RDY mask is cleared (e.g. by writing the value 0x80 to
336  // register R#4). Reading the status register still has the BUF_RDY
337  // bit set. Without this behavior demos like 'NOP Unknown reality'
338  // hang when testing the amount of sample ram or when uploading data
339  // to the sample ram.
340  //
341  // Before this code was added, those demos also worked but only
342  // because we had a hack that always kept bit BUF_RDY set.
343  //
344  // When the ADPCM unit is not performing any function (e.g. after a
345  // reset), the BUF_RDY bit should still be set. The AUDIO detection
346  // routine in 'MSX-Audio BIOS v1.3' depends on this. See
347  // [3533002] Y8950 not being detected by MSX-Audio v1.3
348  // https://sourceforge.net/tracker/?func=detail&aid=3533002&group_id=38274&atid=421861
349  // TODO I've implemented this as '(reg7 & R07_MODE) == 0', is this
350  // correct/complete?
351  if (((reg7 & R07_MODE & ~R07_REC) == R07_MEMORY_DATA) ||
352  ((reg7 & R07_MODE) == 0)){
353  // transfer to or from sample ram, or no function
355  }
356 }
357 
358 byte Y8950Adpcm::readData()
359 {
360  if ((reg7 & R07_MODE) == R07_MEMORY_DATA) {
361  // external memory read
362  assert(!isPlaying()); // no need to update the 'aud' data
363  if (readDelay) {
364  emu.memPntr = startAddr;
365  }
366  }
367  byte result = peekData();
368  if ((reg7 & R07_MODE) == R07_MEMORY_DATA) {
369  assert(!isPlaying()); // no need to update the 'aud' data
370  if (readDelay) {
371  // two dummy reads
372  --readDelay;
374  } else if (emu.memPntr > stopAddr) {
375  // set EOS bit in status register
377  } else {
378  emu.memPntr += 2; // two nibbles at a time
379 
380  // reset BRDY bit in status register, which means we
381  // are reading the memory now
383 
384  // setup a timer that will callback us in 10 master
385  // clock cycles for Y8950. In the callback set the BRDY
386  // flag to 1, which means we have another data ready.
387  // For now, we don't really do this; we simply reset and
388  // set the flag in zero time, so that the IRQ will work.
389 
390  // set BRDY bit in status register
392  }
393  }
394  return result;
395 }
396 
397 byte Y8950Adpcm::peekData() const
398 {
399  if ((reg7 & R07_MODE) == R07_MEMORY_DATA) {
400  // external memory read
401  assert(!isPlaying()); // no need to update the 'aud' data
402  if (readDelay) {
403  return reg15;
404  } else if (emu.memPntr > stopAddr) {
405  return 0;
406  } else {
407  return readMemory(emu.memPntr);
408  }
409  } else {
410  return 0; // TODO check
411  }
412 }
413 
414 void Y8950Adpcm::writeMemory(unsigned memPntr, byte value)
415 {
416  unsigned addr = (memPntr / 2) & addrMask;
417  if ((addr < ram.getSize()) && !romBank) {
418  ram.write(addr, value);
419  }
420 }
421 byte Y8950Adpcm::readMemory(unsigned memPntr) const
422 {
423  unsigned addr = (memPntr / 2) & addrMask;
424  if (romBank || (addr >= ram.getSize())) {
425  return 0; // checked on a real machine
426  } else {
427  return ram[addr];
428  }
429 }
430 
432 {
433  // called by audio thread
434  if (!isPlaying()) return 0;
435  int output = calcSample(false);
436  return (reg7 & R07_SP_OFF) ? 0 : output;
437 }
438 
439 int Y8950Adpcm::calcSample(bool doEmu)
440 {
441  // values taken from ymdelta.c by Tatsuyuki Satoh.
442  static constexpr int F1[16] = { 1, 3, 5, 7, 9, 11, 13, 15,
443  -1, -3, -5, -7, -9, -11, -13, -15 };
444  static constexpr int F2[16] = { 57, 57, 57, 57, 77, 102, 128, 153,
445  57, 57, 57, 57, 77, 102, 128, 153 };
446 
447  assert(isPlaying());
448 
449  PlayData& pd = doEmu ? emu : aud;
450  pd.nowStep += delta;
451  if (pd.nowStep & ~STEP_MASK) {
452  pd.nowStep &= STEP_MASK;
453  byte val;
454  if (!(pd.memPntr & 1)) {
455  // even nibble
456  if (reg7 & R07_MEMORY_DATA) {
457  pd.adpcm_data = readMemory(pd.memPntr);
458  } else {
459  pd.adpcm_data = reg15;
460  // set BRDY bit, ready to accept new data
461  if (doEmu) {
463  }
464  }
465  val = pd.adpcm_data >> 4;
466  } else {
467  // odd nibble
468  val = pd.adpcm_data & 0x0F;
469  }
470  int prevOut = pd.out;
471  pd.out = Math::clipIntToShort(pd.out + (pd.diff * F1[val]) / 8);
472  pd.diff = std::clamp((pd.diff * F2[val]) / 64, DMIN, DMAX);
473 
474  int prevLeveling = pd.nextLeveling;
475  pd.nextLeveling = (prevOut + pd.out) / 2;
476  int deltaLeveling = pd.nextLeveling - prevLeveling;
477  pd.sampleStep = deltaLeveling * volumeWStep;
478  int tmp = deltaLeveling * ((volume * pd.nowStep) >> STEP_BITS);
479  pd.output = prevLeveling * volume + tmp;
480 
481  ++pd.memPntr;
482  if ((reg7 & R07_MEMORY_DATA) &&
483  (pd.memPntr > stopAddr)) {
484  // On 2003/06/21 I commited a patch with comment:
485  // generate end-of-sample interrupt at every sample
486  // end, including loops
487  // Unfortunatly it doesn't give any reason why and now
488  // I can't remember it :-(
489  // This is different from e.g. the MAME implementation.
490  if (doEmu) {
492  }
493  if (reg7 & R07_REPEAT) {
494  restart(pd);
495  } else {
496  if (doEmu) {
497  removeSyncPoint();
498  reg7 = 0;
499  }
500  }
501  }
502  } else {
503  pd.output += pd.sampleStep;
504  }
505  return pd.output >> 12;
506 }
507 
508 
509 // version 1:
510 // Initial verson
511 // version 2:
512 // - Split PlayData in emu and audio part (though this doesn't add new state
513 // to the savestate).
514 // - Added clock object.
515 template<typename Archive>
516 void Y8950Adpcm::serialize(Archive& ar, unsigned version)
517 {
518  ar.template serializeBase<Schedulable>(*this);
519  ar.serialize("ram", ram,
520  "startAddr", startAddr,
521  "stopAddr", stopAddr,
522  "addrMask", addrMask,
523  "volume", volume,
524  "volumeWStep", volumeWStep,
525  "readDelay", readDelay,
526  "delta", delta,
527  "reg7", reg7,
528  "reg15", reg15,
529  "romBank", romBank,
530 
531  "memPntr", emu.memPntr,
532  "nowStep", emu.nowStep,
533  "out", emu.out,
534  "output", emu.output,
535  "diff", emu.diff,
536  "nextLeveling", emu.nextLeveling,
537  "sampleStep", emu.sampleStep,
538  "adpcm_data", emu.adpcm_data);
539  if (ar.isLoader()) {
540  // ignore aud part for saving,
541  // for loading we make it the same as the emu part
542  aud = emu;
543  }
544 
545  if (ar.versionBelow(version, 2)) {
546  clock.reset(getCurrentTime());
547 
548  // reschedule, because automatically deserialized sync-point
549  // can be off, because clock.getTime() != getCurrentTime()
550  removeSyncPoint();
551  if (isPlaying()) {
552  schedule();
553  }
554  } else {
555  ar.serialize("clock", clock);
556  }
557 }
559 
560 } // namespace openmsx
openmsx::Clock::getTicksTill
constexpr unsigned getTicksTill(EmuTime::param e) const
Calculate the number of ticks for this clock until the given time.
Definition: Clock.hh:58
openmsx::R07_MEMORY_DATA
constexpr int R07_MEMORY_DATA
Definition: Y8950Adpcm.cc:26
gl::clamp
vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
Definition: gl_vec.hh:301
openmsx::R07_START
constexpr int R07_START
Definition: Y8950Adpcm.cc:28
Clock.hh
openmsx::TrackedRam::clear
void clear(byte c=0xff)
Definition: TrackedRam.hh:43
openmsx::Y8950Adpcm::calcSample
int calcSample()
Definition: Y8950Adpcm.cc:431
serialize.hh
openmsx::Y8950Adpcm
Definition: Y8950Adpcm.hh:16
openmsx::Y8950Adpcm::Y8950Adpcm
Y8950Adpcm(Y8950 &y8950, const DeviceConfig &config, const std::string &name, unsigned sampleRam)
Definition: Y8950Adpcm.cc:47
openmsx::DeviceConfig
Definition: DeviceConfig.hh:20
openmsx::Y8950Adpcm::isMuted
bool isMuted() const
Definition: Y8950Adpcm.cc:89
openmsx::R08_DA_AD
constexpr int R08_DA_AD
Definition: Y8950Adpcm.cc:34
openmsx::R07_MODE
constexpr int R07_MODE
Definition: Y8950Adpcm.cc:29
gl::length
T length(const vecN< N, T > &x)
Definition: gl_vec.hh:348
openmsx::Y8950::resetStatus
void resetStatus(byte flags)
Definition: Y8950.cc:1207
openmsx::Schedulable
Every class that wants to get scheduled at some point must inherit from this class.
Definition: Schedulable.hh:34
openmsx::Y8950Adpcm::sync
void sync(EmuTime::param time)
Definition: Y8950Adpcm.cc:106
openmsx::R08_NOTE_SET
constexpr int R08_NOTE_SET
Definition: Y8950Adpcm.cc:36
openmsx::R08_SAMPL
constexpr int R08_SAMPL
Definition: Y8950Adpcm.cc:35
Y8950Adpcm.hh
openmsx::Y8950::setStatus
void setStatus(byte flags)
Definition: Y8950.cc:1199
Y8950.hh
openmsx::Y8950Adpcm::reset
void reset(EmuTime::param time)
Definition: Y8950Adpcm.cc:63
openmsx::Y8950::STATUS_PCM_BSY
static constexpr int STATUS_PCM_BSY
Definition: Y8950.hh:48
openmsx::Y8950Adpcm::readReg
byte readReg(byte rg, EmuTime::param time)
Definition: Y8950Adpcm.cc:301
openmsx::R08_CSM
constexpr int R08_CSM
Definition: Y8950Adpcm.cc:37
openmsx::Schedulable::setSyncPoint
void setSyncPoint(EmuTime::param timestamp)
Definition: Schedulable.cc:23
openmsx::R07_RESET
constexpr int R07_RESET
Definition: Y8950Adpcm.cc:23
openmsx::TrackedRam::getSize
unsigned getSize() const
Definition: TrackedRam.hh:20
openmsx::Y8950Adpcm::clearRam
void clearRam()
Definition: Y8950Adpcm.cc:58
openmsx::R07_SP_OFF
constexpr int R07_SP_OFF
Definition: Y8950Adpcm.cc:24
INSTANTIATE_SERIALIZE_METHODS
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:981
openmsx::R07_REPEAT
constexpr int R07_REPEAT
Definition: Y8950Adpcm.cc:25
openmsx::DMAX
constexpr int DMAX
Definition: Y8950Adpcm.cc:39
openmsx::Y8950::STATUS_BUF_RDY
static constexpr int STATUS_BUF_RDY
Definition: Y8950.hh:50
openmsx::R08_ROM
constexpr int R08_ROM
Definition: Y8950Adpcm.cc:32
openmsx::Y8950Adpcm::resetStatus
void resetStatus()
Definition: Y8950Adpcm.cc:333
openmsx::Y8950Adpcm::writeReg
void writeReg(byte rg, byte data, EmuTime::param time)
Definition: Y8950Adpcm.cc:151
openmsx::STEP_MASK
constexpr int STEP_MASK
Definition: Y8950Adpcm.cc:44
openmsx::Y8950::STATUS_EOS
static constexpr int STATUS_EOS
Definition: Y8950.hh:49
openmsx::DMIN
constexpr int DMIN
Definition: Y8950Adpcm.cc:40
openmsx::TrackedRam::write
void write(unsigned addr, byte value)
Definition: TrackedRam.hh:38
openmsx::Clock::reset
constexpr void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
Definition: Clock.hh:102
openmsx::Clock::advance
constexpr void advance(EmuTime::param e)
Advance this clock in time until the last tick which is not past the given time.
Definition: Clock.hh:110
Math::clipIntToShort
int16_t clipIntToShort(int x)
Clip x to range [-32768,32767].
Definition: Math.hh:100
openmsx::DDEF
constexpr int DDEF
Definition: Y8950Adpcm.cc:41
openmsx::Schedulable::getCurrentTime
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
Definition: Schedulable.cc:49
openmsx::Y8950Adpcm::serialize
void serialize(Archive &ar, unsigned version)
Definition: Y8950Adpcm.cc:516
openmsx::R08_64K
constexpr int R08_64K
Definition: Y8950Adpcm.cc:33
openmsx::Y8950Adpcm::peekReg
byte peekReg(byte rg, EmuTime::param time) const
Definition: Y8950Adpcm.cc:310
DeviceConfig.hh
Math.hh
openmsx::Clock
Represents a clock with a fixed frequency.
Definition: Clock.hh:19
openmsx::STEP_BITS
constexpr int STEP_BITS
Definition: Y8950Adpcm.cc:43
openmsx::R07_REC
constexpr int R07_REC
Definition: Y8950Adpcm.cc:27
openmsx
This file implemented 3 utility functions:
Definition: Autofire.cc:5
openmsx::Y8950::peekRawStatus
byte peekRawStatus() const
Definition: Y8950.cc:1215
MSXMotherBoard.hh
openmsx::Schedulable::removeSyncPoint
bool removeSyncPoint()
Definition: Schedulable.cc:28
openmsx::Y8950
Definition: Y8950.hh:24