openMSX
R800.hh
Go to the documentation of this file.
1 #ifndef R800_HH
2 #define R800_HH
3 
4 #include "CPUClock.hh"
5 #include "CPU.hh" // for CPURegs, split header?
6 #include "Clock.hh"
7 #include "likely.hh"
8 #include "inline.hh"
9 
10 namespace openmsx {
11 
12 class R800TYPE : public CPUClock
13 {
14 public:
15  void updateVisiblePage(byte page, byte primarySlot, byte secondarySlot)
16  {
17  extraMemoryDelay[page] =
18  extraMemoryDelays[page][primarySlot][secondarySlot];
19  }
20  void setDRAMmode(bool dram)
21  {
22  // TODO currently hardcoded, move to config file?
23  unsigned val = dram ? 0 : 1;
24  extraMemoryDelays[0][0][0] = val; // BIOS
25  extraMemoryDelays[1][0][0] = val; // BASIC
26  extraMemoryDelays[0][3][1] = val; // SUB-ROM
27  extraMemoryDelays[1][3][1] = val; // KANJI-DRIVER
28  }
29 
30 protected:
31  template<bool B> struct Normalize { static const bool value = B; };
32 
33  static const int CLOCK_FREQ = 7159090;
34 
35  ALWAYS_INLINE unsigned haltStates() const { return 1; } // TODO check this
36  ALWAYS_INLINE bool isR800() const { return true; }
37 
38  R800TYPE(EmuTime::param time, Scheduler& scheduler)
39  : CPUClock(time, scheduler)
40  , lastRefreshTime(time)
41  {
43 
44  // TODO currently hardcoded, move to config file?
45  for (int page = 0; page < 4; ++page) {
46  for (int prim = 0; prim < 4; ++prim) {
47  for (int sec = 0; sec < 4; ++sec) {
48  unsigned val;
49  if ((prim == 1) || (prim == 2)) {
50  // external slot
51  val = 2;
52  } else if ((prim == 3) && (sec == 0)) {
53  // internal RAM
54  val = 0;
55  } else {
56  // internal ROM
57  val = 1;
58  }
59  extraMemoryDelays[page][prim][sec] = val;
60  }
61  }
62  }
63  for (int page = 0; page < 4; ++page) {
64  extraMemoryDelay[page] = extraMemoryDelays[page][0][0];
65  }
66  }
67 
69  {
70  lastPage = -1;
71  }
72 
73  template <bool PRE_PB, bool POST_PB>
74  ALWAYS_INLINE void PRE_MEM(unsigned address)
75  {
76  int newPage = address >> 8;
77  if (PRE_PB) {
78  // there is a statically predictable page break at this
79  // point -> 'add(1)' moved to static cost table
80  } else {
81  if (unlikely(newPage != lastPage) ||
82  unlikely(extraMemoryDelay[address >> 14])) {
83  add(1);
84  }
85  }
86  if (!POST_PB) {
87  lastPage = newPage;
88  }
89  }
90  template <bool POST_PB>
91  ALWAYS_INLINE void POST_MEM(unsigned address)
92  {
93  add(extraMemoryDelay[address >> 14]);
94  if (POST_PB) {
96  }
97  }
98  template <bool PRE_PB, bool POST_PB>
99  ALWAYS_INLINE void PRE_WORD(unsigned address)
100  {
101  int newPage = address >> 8;
102  if (PRE_PB) {
103  // there is a statically predictable page break at this
104  // point -> 'add(1)' moved to static cost table
105  if (unlikely(extraMemoryDelay[address >> 14])) {
106  add(1);
107  }
108  } else {
109  if (unlikely(extraMemoryDelay[address >> 14])) {
110  add(2);
111  } else if (unlikely(newPage != lastPage)) {
112  add(1);
113  }
114  }
115  if (!POST_PB) {
116  lastPage = newPage;
117  }
118  }
119  template <bool POST_PB>
120  ALWAYS_INLINE void POST_WORD(unsigned address)
121  {
122  add(2 * extraMemoryDelay[address >> 14]);
123  if (POST_PB) {
125  }
126  }
127 
129  {
130  // atoc documentation says refresh every 222 clocks
131  // duration: 256/1024KB 13.5 clocks
132  // 512KB 21.5 clocks
133  // But 26/210 matches measurements much better
134  // (loosly based on old measurements by Jon on his analogue scope)
135  EmuTime time = getTimeFast();
136  if (unlikely(lastRefreshTime.getTicksTill_fast(time) >= 210)) {
137  R800RefreshSlow(time, R); // slow-path not inline
138  }
139  }
141  {
142  do {
143  lastRefreshTime += 210;
144  } while (unlikely(lastRefreshTime.getTicksTill_fast(time) >= 210));
145  waitForEvenCycle(0);
146  add(25);
147  R800ForcePageBreak(); // TODO check this
148  R.incR(1);
149  }
150 
152  {
153  // Base class implementation.
154  CPUClock::setTime(time);
155 
156  // Otherwise advance_fast() in R800Refresh() above, gets a too
157  // large time interval.
158  lastRefreshTime.reset(time);
159  }
160 
161  ALWAYS_INLINE void setMemPtr(unsigned) { /* nothing*/ }
162  ALWAYS_INLINE unsigned getMemPtr() const { return 0; } // dummy value
163 
164  static const int I = 6; // cycles for an I/O operation
165  static const int O = 1; // wait for one cycle and wait for next even
166  // clock cycle (to sync with slower IO bus)
167  // the latter part must be implemented
168  // dynamically (not here in static tables)
169  static const int P = 1; // cycles for a (statically known) page-break
170 
171  static const int
178  CC_LD_R_XIX = 3+P+1, CC_LD_R_XIX_1 = 1, CC_LD_R_XIX_2 = 3+P, // +1
183  CC_LD_XIX_R = 3+P+1, CC_LD_XIX_R_1 = 1, CC_LD_XIX_R_2 = 3+P, // +1
184  CC_LD_XIX_N = 3+P+1, CC_LD_XIX_N_1 = 1, CC_LD_XIX_N_2 = 3+P, // +1
189 
190  CC_CP_R = 1,
191  CC_CP_N = 2, CC_CP_N_1 = 1,
192  CC_CP_XHL = 1+P+1, CC_CP_XHL_1 = 1+P,
193  CC_CP_XIX = 3+P+1, CC_CP_XIX_1 = 1, CC_CP_XIX_2 = 3+P, // +1
194  CC_INC_R = 1,
195  CC_INC_XHL = 1+P+2+P+1, CC_INC_XHL_1 = 1, CC_INC_XHL_2 = 1+P+2+P,
196  CC_INC_XIX = 3+P+2+P+1, CC_INC_XIX_1 = 1, EE_INC_XIX = 2, // +1
200 
201  CC_LDI = 2+P+1+P+1, CC_LDI_1 = 2+P, CC_LDI_2 = 2+P+1+P,
202  CC_LDIR = 2+P+1+P+1,
203  CC_CPI = 2+P+2, CC_CPI_1 = 2+P,
204  CC_CPIR = 2+P+3, // TODO check
205 
206  CC_PUSH = 2+P+2, CC_PUSH_1 = 2+P,
207  CC_POP = 1+P+2, CC_POP_1 = 1+P,
208  CC_CALL = 3+P+2, CC_CALL_1 = 1, EE_CALL = 1,
209  CC_CALL_A = 3+P+2, CC_CALL_B = 3,
210  CC_RST = 2+P+2, // TODO check
211  CC_RET_A = 1+P+2, CC_RET_B = 1, EE_RET_C = 0,
212  CC_RETN = 2+P+2, EE_RETN = 1, // TODO check
213  CC_JP_A = 4, CC_JP_B = 3, CC_JP_1 = 1,
214  CC_JP_HL = 2,
215  CC_JR_A = 3, CC_JR_B = 2, CC_JR_1 = 1,
216  CC_DJNZ = 3, EE_DJNZ = 0,
217 
219 
220  CC_BIT_R = 2,
222  CC_BIT_XIX = 3+P+1, CC_BIT_XIX_1 = 3+P, // +1
223  CC_SET_R = 2,
224  CC_SET_XHL = 2+P+2+P+1, CC_SET_XHL_1 = 2+P, CC_SET_XHL_2 = 2+P+2+P,
225  CC_SET_XIX = 3+P+2+P+1, EE_SET_XIX = 1, // +1
226 
227  CC_RLA = 1,
228  CC_RLD = 2+P+2+P+1, CC_RLD_1 = 2+P, CC_RLD_2 = 2+P+2+P,
229 
232  CC_INI = 2+O+I+P+1, CC_INI_1 = 2+O, CC_INI_2 = 2+O+I+P,
233  CC_INIR = 2+O+I+P+1, // TODO check
236  CC_OUTI = 2+P+1+O+I, CC_OUTI_1 = 2+P, CC_OUTI_2 = 2+P+1+O,
237  CC_OTIR = 2+P+1+O+I, // TODO check
238 
239  CC_EX = 1,
240  CC_NOP = 1,
241  CC_CCF = 1,
242  CC_SCF = 1,
243  CC_DAA = 1,
244  CC_NEG = 2,
245  CC_CPL = 1,
246  CC_DI = 2,
247  CC_EI = 1,
248  CC_HALT = 1, // TODO check
249  CC_IM = 3,
250 
251  CC_MULUB = 14,
252  CC_MULUW = 36,
253 
254  CC_NMI = 1+P+2, EE_NMI_1 = -1, // TODO check
255  CC_IRQ0 = 1+P+2, EE_IRQ0_1 = -1, // TODO check
256  CC_IRQ1 = 1+P+2, EE_IRQ1_1 = -1, // TODO check
257  CC_IRQ2 = 1+P+2+P+2, EE_IRQ2_1 = -1, CC_IRQ2_2 = 1+P+2+P, // TODO check
258 
259  CC_MAIN = 0,
260  CC_DD = 1,
262  CC_DD_CB = 1, // +1
263  EE_ED = 1,
264  CC_RDMEM = 1,
265  CC_WRMEM = 2;
266 
267  template<typename Archive>
268  void serialize(Archive& ar, unsigned version)
269  {
270  CPUClock::serialize(ar, version);
271  ar.serialize("lastRefreshTime", lastRefreshTime);
272  ar.serialize("lastPage", lastPage);
273  ar.serialize("extraMemoryDelay", extraMemoryDelay);
274 
275  // don't serialize 'extraMemoryDelays', is initialized in
276  // constructor and setDRAMmode()
277  }
278 
279 private:
280  Clock<CLOCK_FREQ> lastRefreshTime;
281  int lastPage;
282 
283  unsigned extraMemoryDelays[4][4][4];
284  unsigned extraMemoryDelay[4];
285 };
286 
287 } // namespace openmsx
288 
289 #endif