| 1 |
Goal of the experiment |
| 2 |
====================== |
| 3 |
|
| 4 |
On a MSX2+ machine or a turboR machine in Z80 mode, there are extra wait cycles |
| 5 |
when accessing a VDP IO-port (0x98-0x9b) compared to other IO ports (these are |
| 6 |
introduced by the T9769{A,B,C} chip). |
| 7 |
|
| 8 |
In openMSX we also added those wait cycles in R800 mode. That's likely wrong. |
| 9 |
We verify this hypothesis in this experiment. Spoiler: in R800 mode there are |
| 10 |
indeed no extra wait cycles. |
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
Sub-goals |
| 15 |
========= |
| 16 |
|
| 17 |
I don't have my real MSX computers permanently connected anymore. When I do set |
| 18 |
them up, it's not too much extra effort to run a few more tests. |
| 19 |
|
| 20 |
Emulation of Z80 timing is pretty straight forward. R800 is more complex. We |
| 21 |
know about the following peculiarities: |
| 22 |
- Align to even cycle on IO instructions (because R800, which runs at 7MHz, |
| 23 |
needs to access the IO bus, which runs at 3.5MHz). |
| 24 |
- Doing two accesses to VDP IO-ports introduces a delay when those accesses are |
| 25 |
"too close together". |
| 26 |
- Refresh behavior. Every so many cycles (current estimate: 210 cycles @7MHz), |
| 27 |
normal R800 instruction execution gets interrupted for a refresh period |
| 28 |
(current estimate: this period lasts 25.5 cycles. Best guess for that half |
| 29 |
cycle: because 50% chance it needs to align to an even cycle). |
| 30 |
|
| 31 |
|
| 32 |
|
| 33 |
Design of the experiment |
| 34 |
======================== |
| 35 |
|
| 36 |
I (repeatedly) run test programs with the following structure: |
| 37 |
|
| 38 |
DI |
| 39 |
OUT (#20),A ; reset counter |
| 40 |
|
| 41 |
<body> |
| 42 |
|
| 43 |
IN A,(#20) ; latch counter, and read 32-bit value in [DE:HL] |
| 44 |
LD L,A |
| 45 |
IN A,(#21) |
| 46 |
LD H,A |
| 47 |
IN A,(#22) |
| 48 |
LD E,A |
| 49 |
IN A,(#23) |
| 50 |
LD D,A |
| 51 |
EI |
| 52 |
RET |
| 53 |
|
| 54 |
Where <body> varies from test-to-test. |
| 55 |
|
| 56 |
Alex Wulms gave me a FPGA test board that implements a 32-bit counter which |
| 57 |
runs at 3.5MHz and is accessed via IO-ports 0x20-0x23. In openMSX this device |
| 58 |
is emulated as the 'hirestimer' extension. |
| 59 |
- Writing (any) value to port 0x20 resets the counter to zero. |
| 60 |
- That counter increments at a frequency of 3.5MHz (because the MSX IO-bus runs |
| 61 |
at this frequency). |
| 62 |
- Reading IO port 0x20 latches the value of the counter and returns the 8 LSB |
| 63 |
bits. |
| 64 |
- Reading IO ports 0x21-0x23 retrieves the other bits of the latched value. |
| 65 |
|
| 66 |
So in Z80 mode (runs at 3.5MHz) this board allows to make cycle-accurate |
| 67 |
measurements. But the R800 runs at 7MHz, so we can only measure up-to 2 cycles |
| 68 |
accurate. |
| 69 |
|
| 70 |
The test code is located at the start of a 256-byte block (specifically at |
| 71 |
address 0x0100 in my case). This ensures that none of the instruction fetches |
| 72 |
take an extra cycle for the "page-break". |
| 73 |
|
| 74 |
I ran all tests on a turbor-GT machine, both in Z80 and R800 mode. |
| 75 |
|
| 76 |
(Not important) I ran all tests in compass, because that allows me to easily: |
| 77 |
- Edit the test program |
| 78 |
- Re-assemble (CTRL-A) |
| 79 |
- Execute (CTRL-G) |
| 80 |
- Look at the result: the value in registers DE:HL (F4) |
| 81 |
|
| 82 |
|
| 83 |
|
| 84 |
The actual tests |
| 85 |
================ |
| 86 |
|
| 87 |
In this section I'll present _processed_ test results. Because often the |
| 88 |
details are not important for our main goal and too detailed results would only |
| 89 |
add confusion. However these details could be important to (in the future) |
| 90 |
investigate the sub-goals. Therefor I still included the raw results in an |
| 91 |
appendix. |
| 92 |
|
| 93 |
|
| 94 |
1) Establish a base-line |
| 95 |
------------------------ |
| 96 |
|
| 97 |
Replace <body> with a sequence of n NOP instructions. |
| 98 |
|
| 99 |
n | Z80 | R800 |
| 100 |
---+-----+----- |
| 101 |
0 | 12 | 5 |
| 102 |
1 | 17 | 5 |
| 103 |
2 | 22 | 6 |
| 104 |
3 | 27 | 6 |
| 105 |
4 | 32 | 7 |
| 106 |
5 | 37 | 7 |
| 107 |
|
| 108 |
The case n=0 (an empty <body>) is important. It measures the time between |
| 109 |
resetting the counter and immediately after reading it back. Thus |
| 110 |
OUT (#20),A ; reset counter |
| 111 |
IN A,(#20) ; read it back |
| 112 |
|
| 113 |
This means that for the actual duration of the <body>, we must subtract this |
| 114 |
n=0 result. |
| 115 |
|
| 116 |
On a Z80 both an 'OUT (n),A' and 'IN A,(n)' instruction take 12 cycles. So the |
| 117 |
value 12 in the above table makes sense. (Actually it also depends where |
| 118 |
exactly within the OUT instruction the counter gets reset and where exactly the |
| 119 |
counter gets latched, but apparently that happens at the same relative offsets |
| 120 |
in the OUT/IN instructions). |
| 121 |
|
| 122 |
On Z80, for the n>=1 cases we then get the, totally as expected, result that a |
| 123 |
NOP instruction takes 5 cycles. |
| 124 |
|
| 125 |
For R800 n=0 we measure 5 cycles at 3.5MHz, this could be either 9 or 10 cycles |
| 126 |
at 7MHz. In our current R800 emulation model an IN or OUT instruction takes 9 |
| 127 |
cycles plus an optional cycle to 'align' the 7MHz R800-bus with the 3.5MHz |
| 128 |
IO-bus. So that would be 10 cycles @7MHz in this case which matches the |
| 129 |
measured value. |
| 130 |
|
| 131 |
The case R800 n=1 also measures 5, that's because the cost of the NOP |
| 132 |
instruction is "absorbed" by no longer having to wait for 1 cycle to align both |
| 133 |
buses. And also for n>=2, the measured value only goes up every 2nd increment |
| 134 |
of n. |
| 135 |
|
| 136 |
|
| 137 |
2a) Measure a single OUT instruction, non-VDP port |
| 138 |
-------------------------------------------------- |
| 139 |
|
| 140 |
In these group of tests the body of our test program is |
| 141 |
NOP, repeated m times |
| 142 |
OUT (#30),A |
| 143 |
NOP, repeated n times |
| 144 |
|
| 145 |
I arbitrarily picked IO port 0x30 because it has no function and because (I |
| 146 |
hoped) it introduces no extra wait cycles when accessing it. I put a variable |
| 147 |
amount (possibly zero) of NOP instruction both in front and after the OUT |
| 148 |
instruction. |
| 149 |
|
| 150 |
Here are the results: |
| 151 |
|
| 152 |
m | n | Z80 | R800 |
| 153 |
---+---+-----+------------ |
| 154 |
0 | 0 | 24 | 10 (sometimes 22) |
| 155 |
0 | 1 | 29 | 10 (sometimes 23) |
| 156 |
0 | 2 | 34 | 11 (sometimes 23) |
| 157 |
0 | 3 | 39 | 11 (sometimes 24) |
| 158 |
0 | 4 | 44 | 12 (sometimes 24) |
| 159 |
0 | 5 | 49 | 12 (sometimes 25) |
| 160 |
--+---+-----+------------ |
| 161 |
1 | 0 | 29 | 10 (*) |
| 162 |
1 | 1 | 34 | 10 (*) |
| 163 |
1 | 2 | 39 | 11 (sometimes 23,24,25) |
| 164 |
--+---+-----+------------ |
| 165 |
2 | 0 | 34 | 11 (sometimes 23) |
| 166 |
2 | 1 | 39 | 11 (sometimes 24) |
| 167 |
2 | 2 | 44 | 12 (sometimes 24,25) |
| 168 |
|
| 169 |
(*) The lack of a 'sometimes' annotation does not mean it never occurs, |
| 170 |
instead it probably means I didn't measure long enough to observe it. |
| 171 |
|
| 172 |
Z80 is completely as expected. Here's the formula that predicts the result: |
| 173 |
12 + 12 + 5*(m+n) |
| 174 |
|
| 175 |
The 'normal' R800 values (ignoring the 'sometimes' annotations) can also be |
| 176 |
explained. There are now two IO instructions that can 'absorb' the cost of a |
| 177 |
NOP. Thus again the measured value only goes up by 1 for every 2nd increment of |
| 178 |
'm' or 'n' independently. |
| 179 |
|
| 180 |
Now for the 'sometimes' values. For example for R800 n=0 we mostly measured |
| 181 |
value 11, but from time time instead we measured the value 23. Very roughly |
| 182 |
speaking, the chance of measuring the alternative value went up with the |
| 183 |
duration of the test (so mostly visible in test 3b with high 'n' values). |
| 184 |
|
| 185 |
This phenomenon can be explained by the R800 refresh stuff. At regular |
| 186 |
intervals the R800 gets interrupted from normal instruction execution for |
| 187 |
refresh. If such an interruption falls in the test then we'll measure a higher |
| 188 |
duration. The value of this extra duration is 12 or 13 (glossing over the m=1, |
| 189 |
n=2 result). This corresponds with ~25 R800 cycles. (This matches the current |
| 190 |
emulation model). |
| 191 |
|
| 192 |
|
| 193 |
2b) Measure a single OUT instruction, VDP port |
| 194 |
---------------------------------------------- |
| 195 |
|
| 196 |
This is the most important test in this experiment (from the point of view of |
| 197 |
the original goal). It's the same test as in 2a) but with port 0x30 replaced |
| 198 |
with 0x98. |
| 199 |
|
| 200 |
m | n | Z80 | R800 |
| 201 |
---|---+-----+------------ |
| 202 |
0 | 0 | 25 | 10 (sometimes 22) |
| 203 |
0 | 1 | 30 | 10 (sometimes 23) |
| 204 |
0 | 2 | 35 | 11 (sometimes 23) |
| 205 |
0 | 3 | 40 | 11 (sometimes 24) |
| 206 |
0 | 4 | 45 | 12 (sometimes 24) |
| 207 |
0 | 5 | 50 | 12 (sometimes 25) |
| 208 |
--+---+-----+------------ |
| 209 |
1 | 0 | 30 | 10 (sometimes 22) |
| 210 |
1 | 1 | 35 | 10 (sometimes 23) |
| 211 |
1 | 2 | 40 | 11 (sometimes 23,25) |
| 212 |
--+---+-----+------------ |
| 213 |
2 | 0 | 35 | 11 (sometimes 23) |
| 214 |
2 | 1 | 40 | 11 (*) |
| 215 |
2 | 2 | 45 | 12 (*) |
| 216 |
|
| 217 |
Comparing 2a) with 2b) gives: |
| 218 |
- Identical results for R800. |
| 219 |
- For Z80 2b) is exactly 1 cycle higher than 2a). |
| 220 |
|
| 221 |
Thus we can confirm our hypothesis: |
| 222 |
- On a turboR in Z80 mode, accessing IO-port 0x98-9b introduces 1 wait cycle. |
| 223 |
- But doing the same in R800 mode does not. |
| 224 |
|
| 225 |
|
| 226 |
|
| 227 |
3a) Two OUT instructions, VDP-port followed by non-VDP port |
| 228 |
----------------------------------------------------------- |
| 229 |
|
| 230 |
Ok, so in the previous test we've already accomplished our goal. But we know |
| 231 |
that there's something else going on in R800 (but not in Z80) mode, namely wait |
| 232 |
cycles between VDP accesses that are "too close together". While we're add it, |
| 233 |
let's also do some measurements on those. |
| 234 |
|
| 235 |
To get a new base-line, we first measure access to a VDP-port followed by |
| 236 |
access to a non-VDP port. More specifically, the new test body is: |
| 237 |
OUT (#98),A |
| 238 |
NOP, repeated n times |
| 239 |
OUT (#30),A |
| 240 |
|
| 241 |
Which results in: |
| 242 |
n | Z80 | R800 |
| 243 |
---+-----+------------------ |
| 244 |
0 | 37 | 15 (sometimes 27) |
| 245 |
1 | 42 | 15 (sometimes 28) |
| 246 |
2 | 47 | 16 (sometimes 28) |
| 247 |
|
| 248 |
|
| 249 |
3b) Two VDP-OUT instructions |
| 250 |
---------------------------- |
| 251 |
|
| 252 |
Now the more interesting test, we make the test body: |
| 253 |
OUT (#98),A |
| 254 |
NOP, repeated n times |
| 255 |
OUT (#98),A |
| 256 |
|
| 257 |
And we get: |
| 258 |
|
| 259 |
n | Z80 | R800 |
| 260 |
---+-----+------------------------- |
| 261 |
0 | 38 | 41 (sometimes 53) |
| 262 |
1 | 43 | 41 (sometimes 53) |
| 263 |
2 | 48 | 41 (sometimes 53) |
| 264 |
5 | 63 | 41 (sometimes 53) |
| 265 |
10 | 88 | 41 (sometimes 53) |
| 266 |
20 | 138 | 41 (sometimes 53) |
| 267 |
30 | 188 | 41 (sometimes 42,53) |
| 268 |
40 | 238 | 41 (sometimes 47,48,53) |
| 269 |
50 | 288 | 41 (sometimes 52,53) |
| 270 |
51 | 293 | 41 (sometimes 53) |
| 271 |
52 | 298 | 41 (sometimes 53,54) |
| 272 |
53 | 303 | 41 (sometimes 54,55) |
| 273 |
54 | 308 | 42 (sometimes 54,55) |
| 274 |
55 | 313 | 42 (sometimes 55,56) |
| 275 |
56 | 318 | 43 (sometimes 55,56) |
| 276 |
57 | 323 | 43 (sometimes 56,57) |
| 277 |
58 | 328 | 44 (sometimes 56,57) |
| 278 |
59 | 333 | 44 (sometimes 56,57,58) |
| 279 |
60 | 338 | 45 (sometimes 57,58) |
| 280 |
65 | 363 | 47 (sometimes 59,60,61) |
| 281 |
70 | 388 | 50 (sometimes 62,63) |
| 282 |
(*) for higher values of 'n' the qualification 'sometimes' becomes more and |
| 283 |
more frequent |
| 284 |
|
| 285 |
The trend is clear. For n<=53 the output remains constant. For n>53 the output |
| 286 |
is again linearly rising, and in fact following the same curve as for non-VDP |
| 287 |
ports. |
| 288 |
|
| 289 |
If you work out the details of how many wait cycles are added, you'll find that |
| 290 |
it matches pretty well with the current openMSX emulation model: openMSX |
| 291 |
requires at least 62 cycles @7MHz between two consecutive VDP IO access. (TODO |
| 292 |
see next chapter. |
| 293 |
|
| 294 |
|
| 295 |
|
| 296 |
TODO Comparison with openMSX |
| 297 |
============================ |
| 298 |
|
| 299 |
TODO repeat these experiments in openMSX and compare. |
| 300 |
[I plan to work on this soon, but need to take a break and want already to |
| 301 |
publish this.] |
| 302 |
|
| 303 |
|
| 304 |
|
| 305 |
|
| 306 |
Appendix: raw test measurements |
| 307 |
=============================== |
| 308 |
|
| 309 |
Notes: |
| 310 |
- Test results are written as hex values (all other values are decimal) |
| 311 |
- Comma separated values within one cell are repeated measurements of the same |
| 312 |
test. |
| 313 |
- In some cases I did not repeated a test, in other cases many times. This is |
| 314 |
based on when I 'guessed' the variation in the result was interesting. Or |
| 315 |
when I was expecting some variation, but didn't get it in the initial |
| 316 |
measurements, I kept measuring until I did see a different result. |
| 317 |
- Though that does mean there is some bias in the results. E.g. you cannot |
| 318 |
use these results to get an accurate estimate of the frequency at which the |
| 319 |
different values occur. |
| 320 |
|
| 321 |
|
| 322 |
1) No (extra) OUT instructions |
| 323 |
<body> = |
| 324 |
NOP, repeated n times |
| 325 |
|
| 326 |
n | Z80 | R800 |
| 327 |
--+-------+----- |
| 328 |
0 | c | 5 |
| 329 |
1 | 11,11 | 5,5 |
| 330 |
2 | 16,16 | 6,6 |
| 331 |
3 | 1b,1b | 6,6 |
| 332 |
4 | 20,20 | 7,7 |
| 333 |
5 | 25,25 | 7,7 |
| 334 |
|
| 335 |
|
| 336 |
|
| 337 |
2a) Single OUT instruction, non-VDP port |
| 338 |
<body> = |
| 339 |
NOP, repeated m times |
| 340 |
OUT (#30),A |
| 341 |
NOP, repeated n times |
| 342 |
|
| 343 |
m | n | Z80 | R800 |
| 344 |
--+---+-------+------------ |
| 345 |
0 | 0 | 18,18 | 16,a,a,a,16 |
| 346 |
0 | 1 | 1d | a,a,17,a,a |
| 347 |
0 | 2 | 22 | b,b,b,b,b,17 |
| 348 |
0 | 3 | 27,27 | b,b,b,b,18 |
| 349 |
0 | 4 | 2c | 18,c,c,c |
| 350 |
0 | 5 | 31,31 | c,19,19,c,c |
| 351 |
--+---+-------+------------ |
| 352 |
1 | 0 | 1d,1d | a,a,a,a,a,a,a ... (46 measurements) |
| 353 |
1 | 1 | 22 | a,a,a,a,a,a,a ... (11 measurements) |
| 354 |
1 | 2 | 27 | b,b,b,b,b,19,18,17,17 |
| 355 |
--+---+-------+------------ |
| 356 |
2 | 0 | 22 | b,b,b,b,17 |
| 357 |
2 | 1 | 27 | b,b,b,b,b,b,b,18 |
| 358 |
2 | 2 | 2c | 19,c,c,c,18,c |
| 359 |
|
| 360 |
|
| 361 |
2b) Single OUT instruction, VDP port |
| 362 |
<body> = |
| 363 |
NOP, repeated m times |
| 364 |
OUT (#98),A |
| 365 |
NOP, repeated n times |
| 366 |
|
| 367 |
m | n | Z80 | R800 |
| 368 |
--|---+-------+------------ |
| 369 |
0 | 0 | 19,19 | a,a,a,a,16 |
| 370 |
0 | 1 | 1e | a,a,a,17 |
| 371 |
0 | 2 | 23 | b,b,b,17 |
| 372 |
0 | 3 | 28 | b,b,b,b,b,18 |
| 373 |
0 | 4 | 2d | c,18,c |
| 374 |
0 | 5 | 32 | c,19,c,c |
| 375 |
--+---+-------+------------ |
| 376 |
1 | 0 | 1e | a,a,a,16,a,a |
| 377 |
1 | 1 | 23 | a,17,a,a,17 |
| 378 |
1 | 2 | 28 | b,b,b,b,b,b,b,b,19,17,b,b |
| 379 |
--+---+-------+------------ |
| 380 |
2 | 0 | 23 | b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,17,17,b |
| 381 |
2 | 1 | 28 | b,b,b,b,b,b,b |
| 382 |
2 | 2 | 2d | c,c,c,c,c,c,c,c,c,c |
| 383 |
|
| 384 |
|
| 385 |
|
| 386 |
3a) Two OUT instructions, VDP-port followed by non-VDP port |
| 387 |
<body> = |
| 388 |
OUT (#98),A |
| 389 |
NOP, repeated n times |
| 390 |
OUT (#30),A |
| 391 |
|
| 392 |
n | Z80 | R800 |
| 393 |
--+-------+------------------ |
| 394 |
0 | 25,25 | f,1b,f,f,f,f |
| 395 |
1 | 2a | 1c,f,f,f,f |
| 396 |
2 | 2f | 1c,10,10,1c,10,10 |
| 397 |
|
| 398 |
|
| 399 |
3b) Two OUT instructions, VDP-port followed by another VDP port |
| 400 |
<body> = |
| 401 |
OUT (#98),A |
| 402 |
NOP, repeated n times |
| 403 |
OUT (#98),A |
| 404 |
|
| 405 |
n | Z80 | R800 |
| 406 |
---+--------+------------------------- |
| 407 |
0 | 26,26 | 29,29,29,35,29,29,29 |
| 408 |
1 | 2b | 29,29,35,29,29,29,35 |
| 409 |
2 | 30 | 29,35,29,35,29,29,29 |
| 410 |
5 | 3f | 35,29,29,29,29 |
| 411 |
10 | 58 | 29,29,35,29,29,29,35 |
| 412 |
20 | 8a | 29,29,29,29,35 |
| 413 |
30 | bc,bc | 2a,29,35,29,29,29,29,29 |
| 414 |
40 | ee,ee | 29,29,30,35,29,29,2f,30,29,29,29,29,2f (30 not a typo!) |
| 415 |
50 | 120,120| 35,29,34,29,29,29,29,34,29,29,29,25 (34 not a typo!) |
| 416 |
51 | 125 | 29,35,29,29,29,29,29,35,35,29,29 |
| 417 |
52 | 12a | 36,29,35,29,29,29,36,29,36,29,36,29,36,35 |
| 418 |
53 | 12f,12f| 29,29,29,36,29,37,29,29,29,29,36,36,29,29 |
| 419 |
54 | 134 | 2a,2a,2a,2a,2a,2a,37,2a,2a,2a,36,2a,2a,2a,37,2a,2a,2a,36,2a,37 |
| 420 |
55 | 139 | 2a,38,36,38,2a,38,37,2a,37,2a,38,36,2a,37,2a,38 |
| 421 |
56 | 13e | 2b,2b,2b,2b,2b,38,37,2b,2b,37,2b,2b,38 |
| 422 |
57 | 143 | 39,37,37,38,2b,39,38,2b,2b,2b,38,2b,2b,2b,38 |
| 423 |
58 | 148 | 38,39,2c,38,2c,2c,2c,2c,39,38,38,2c,2c,2c,2c,39 |
| 424 |
59 | 14d | 39,2c,39,39,39,2c,38,3a,39,2c,2c,39,2c,2c,2c |
| 425 |
60 | 152,152| 39,2d,39,2d,3a,2d,3a,39,2d,39,2d |
| 426 |
65 | 16b | 3d,3b,3c,3b,2f,2f,3d,3c,3b,2f,2f,2f,3c,2f |
| 427 |
70 | 184,184| 3e,3e,3f,32,3e,3e,32,32,3f,32,3f,32,3e,3f,32 |