These results were compiled using an original European model 1 Mega Drive (PAL, no bios or TMSS), and an original Japanese model 1 Mega Drive (NTSC, no bios or TMSS, cartridge lock). The US mega drive should match the Japanese results, since it's the same VDP chip, and it's also running in NTSC mode.
First of all, here are my notes. Here are the results when no interlacing is enabled:
Code: Select all
//----------------------------------------------------------------------------------------
//| Video |PAL |PAL |PAL |PAL |
//| Mode |H32 (RSx=00) |H32 (RSx=00) |H40 (RSx=11) |H40 (RSx=11) |
//| |V28 (M2=0) |V30 (M2=1) |V28 (M2=0) |V30 (M2=1) |
//| Test |Int none(LSMx=*0)|Int none(LSMx=*0)|Int none(LSMx=*0)|Int none(LSMx=*0)|
//|--------------------------------------------------------------------------------------|
//|HCounter |[1]0x00-0x93, |<Same as H32V28> |[1]0x00-0xB6, |<Same as H40V28> |
//|progression |[2]0xE9-0xFF | |[2]0xE4-0xFF | |
//|--------------------------------------------------------------------------------------|
//|VCounter |HCounter changes | |HCounter changes | |
//|increment |from 0x84 to 0x85|<Same as H32V28> |from 0xA4 to 0xA5|<Same as H40V28> |
//| |in [1]. | |in [1]. | |
//|--------------------------------------------------------------------------------------|
//|HBlank set |HCounter changes | |HCounter changes | |
//| |from 0x92 to 0x93|<Same as H32V28> |from 0xB2 to 0xB3|<Same as H40V28> |
//| |at end of [1]. | |in [1]. | |
//|--------------------------------------------------------------------------------------|
//|HBlank cleared|HCounter changes | |HCounter changes | |
//| |from 0x04 to 0x05|<Same as H32V28> |from 0x05 to 0x06|<Same as H40V28> |
//| |in [1]. | |in [1]. | |
//|--------------------------------------------------------------------------------------|
//|VCounter |[1]0x00-0xFF, |[1]0x00-0xFF, | | |
//|progression |[2]0x00-0x02, |[2]0x00-0x0A, |<Same as H32V28> |<Same as H32V30> |
//| |[3]0xCA-0xFF |[3]0xD2-0xFF | | |
//|--------------------------------------------------------------------------------------|
//|VBlank set |Vcounter changes |Vcounter changes | | |
//| |from 0xDF to 0xE0|from 0xEF to 0xF0|<Same as H32V28> |<Same as H32V30> |
//| |in [1]. |in [1]. | | |
//|--------------------------------------------------------------------------------------|
//|VBlank cleared|Vcounter changes |Vcounter changes | | |
//| |from 0xFE to 0xFF|from 0xFE to 0xFF|<Same as H32V28> |<Same as H32V30> |
//| |in [3]. |in [3]. | | |
//|--------------------------------------------------------------------------------------|
//|F flag set |HCounter changes |HCounter changes | | |
//| |from 0x00 to 0x01|from 0x00 to 0x01| | |
//| |at start of [1], |at start of [1], |<Same as H32V28> |<Same as H32V30> |
//| |while VCounter is|while VCounter is| | |
//| |set to 0xE0 in |set to 0xF0 in | | |
//| |[1]. |[1]. | | |
//----------------------------------------------------------------------------------------
Code: Select all
//----------------------------------------------------------------------------------------
//| Video |NTSC |NTSC |NTSC |NTSC |
//| Mode |H32 (RSx=00) |H32 (RSx=00) |H40 (RSx=11) |H40 (RSx=11) |
//| |V28 (M2=0) |V30 (M2=1) |V28 (M2=0) |V30 (M2=1) |
//| Test |Int none(LSMx=*0)|Int none(LSMx=*0)|Int none(LSMx=*0)|Int none(LSMx=*0)|
//|--------------------------------------------------------------------------------------|
//|HCounter |[1]0x00-0x93, |<Same as H32V28> |[1]0x00-0xB6, |<Same as H40V28> |
//|progression |[2]0xE9-0xFF | |[2]0xE4-0xFF | |
//|--------------------------------------------------------------------------------------|
//|VCounter |HCounter changes | |HCounter changes | |
//|increment |from 0x84 to 0x85|<Same as H32V28> |from 0xA4 to 0xA5|<Same as H40V28> |
//| |in [1]. | |in [1]. | |
//|--------------------------------------------------------------------------------------|
//|HBlank set |HCounter changes | |HCounter changes | |
//| |from 0x92 to 0x93|<Same as H32V28> |from 0xB2 to 0xB3|<Same as H40V28> |
//| |at end of [1]. | |in [1]. | |
//|--------------------------------------------------------------------------------------|
//|HBlank cleared|HCounter changes | |HCounter changes | |
//| |from 0x04 to 0x05|<Same as H32V28> |from 0x05 to 0x06|<Same as H40V28> |
//| |in [1]. | |in [1]. | |
//|--------------------------------------------------------------------------------------|
//|VCounter |[1]0x00-0xEA |[1]0x00-0xFF | | |
//|progression |[2]0xE5-0xFF |[2]0x00-0xFF |<Same as H32V28> |<Same as H32V30> |
//| | | | | |
//|--------------------------------------------------------------------------------------|
//|VBlank set |Vcounter changes |Vcounter changes | | |
//| |from 0xDF to 0xE0|from 0xEF to 0xF0|<Same as H32V28> |<Same as H32V30> |
//| |in [1]. |in [1]. | | |
//|--------------------------------------------------------------------------------------|
//|VBlank cleared|Vcounter changes |Vcounter changes | | |
//| |from 0xFE to 0xFF|from 0xFE to 0xFF|<Same as H32V28> |<Same as H32V30> |
//| |in [2]. |in [2]. | | |
//|--------------------------------------------------------------------------------------|
//|F flag set |HCounter changes |HCounter changes | | |
//| |from 0x00 to 0x01|from 0x00 to 0x01| | |
//| |at start of [1], |at start of [1], |<Same as H32V28> |<Same as H32V30> |
//| |while VCounter is|while VCounter is| | |
//| |set to 0xE0 in |set to 0xF0 in | | |
//| |[1]. |[1]. | | |
//----------------------------------------------------------------------------------------
-The only effect that any of the interlace modes have on the status register and HV counter is that the VCounter progression is different, and the ODD flag in the status register is now toggled at the appropriate point. All other properties remain the same as when interlace mode is disabled, so you can use the tables above for those properties regardless of the interlace mode settings. The tables below only list the things that are different when interlacing is enabled.
-When the LSM0 bit was cleared, LSM1 had no noticeable effect on the hv counter or the status register, so all the settings above apply whenever LSM0 is unset.
-You will see both "internal" and an "external" vcounter values listed for the interlace modes below. The VDP has an internal vcounter which has a particular value. Normally, the value the VDP outputs through the HVCounter port matches this internal data, but when interlacing is enabled, this is no longer the case. When emulating the VDP, you should use the "internal" vcounter progression to record the current vcounter location. The "external" vcounter progression is provided for reference. The way each interlace mode derives its external vcounter progression from the internal vcounter differs. The method for generating the modified external vcounter is described below. Note that if you don't correctly emulate the method the VDP is using to derive the external vcounter from the internal vcounter, you might not correctly emulate the "jumps" that the VCounter can appear to make when interlace mode is enabled or disabled, and the output from the existing internal vcounter data is modified based on the new interlace settings.
-The vcounter in normal interlace mode behaves differently to no interlace mode. When outputting the vcounter value, the lowest bit of the vcounter is dropped, and in its place, a normally inaccessible 9th bit of the internal vcounter is shown. This 9th bit is toggled when the VCounter overflows, or in other words, whenever it passes 0xFF and wraps back to 0x00. The lowest bit is being substituted with the highest bit on the VCounter output to provide the most useful information to the programmer within the 8-bit vcounter register size which is externally accessible.
-The vcounter in double interlace mode works differently to normal interlace mode. When outputting the vcounter value, instead of showing the 9th bit of the vcounter in the lowest bit of the output, the VDP shows the lower 7 bits of the internal vcounter mapped to the upper 7 bits of the output vcounter, so that an internal value of 0xE0 is output as 0xC0. The lowest bit represents the 8th bit of the internal vcounter, so an internal count which goes from 0x00-0xFF appears to go from 0x00-0xFE, then from 0x01-0xFF. The 9th vcounter bit never comes into play.
Results from normal interlace mode:
Code: Select all
//----------------------------------------------------------------------------------------
//| Video |PAL |PAL |PAL |PAL |
//| Mode |H32 (RSx=00) |H32 (RSx=00) |H40 (RSx=11) |H40 (RSx=11) |
//| |V28 (M2=0) |V30 (M2=1) |V28 (M2=0) |V30 (M2=1) |
//| Test |Int norm(LSMx=01)|Int norm(LSMx=01)|Int norm(LSMx=01)|Int norm(LSMx=01)|
//|--------------------------------------------------------------------------------------|
//|VCounter |[1]0x00-0xFE(E*2)|[1]0x00-0xFE(E*2)| | |
//|progression |[2]0x01-0x01(O*2)|[2]0x01-0x09(O*2)|<Same as H32V28> |<Same as H32V30> |
//|(external) |[3]0xC9(#ODD) |[3]0xD1(#ODD) | | |
//| |[4]0xCB-0xFF(O*2)|[4]0xD3-0xFF(O*2)| | |
//|--------------------------------------------------------------------------------------|
//|VCounter |[1]0x00-0xFF(*T),|[1]0x00-0xFF(*T),| | |
//|progression |[2]0x00-0x01, |[2]0x00-0x09, |<Same as H32V28> |<Same as H32V30> |
//|(internal) |[3]0xC9(#ODD), |[3]0xD1(#ODD) | | |
//| |[4]0xCA-0xFF(*T),|[4]0xD2-0xFF(*T) | | |
//|--------------------------------------------------------------------------------------|
//|ODD flag |Same time F flag |Same time F flag | | |
//|toggled |set, which is the|set, which is the| | |
//| |same as without |same as without | | |
//| |interlacing. |interlacing. | | |
//| |HCounter changes |HCounter changes |<Same as H32V28> |<Same as H32V30> |
//| |from 0x00 to 0x01|from 0x00 to 0x01| | |
//| |at start of [1], |at start of [1], | | |
//| |while VCounter is|while VCounter is| | |
//| |set to 0xE0 in |set to 0xF0 in | | |
//| |[1]. |[1]. | | |
//----------------------------------------------------------------------------------------
O*2 - Runs odd values in the progression only, and keeps the same value for two complete HCounter progression cycles.
#ODD - Runs only when the ODD flag is set, and for one HCounter progression cycle only.
*T - Toggles the internal 9th VCounter bit when the internal VCounter overflows from 0xFF to 0x00.
Code: Select all
//----------------------------------------------------------------------------------------
//| Video |NTSC |NTSC |NTSC |NTSC |
//| Mode |H32 (RSx=00) |H32 (RSx=00) |H40 (RSx=11) |H40 (RSx=11) |
//| |V28 (M2=0) |V30 (M2=1) |V28 (M2=0) |V30 (M2=1) |
//| Test |Int norm(LSMx=01)|Int norm(LSMx=01)|Int norm(LSMx=01)|Int norm(LSMx=01)|
//|--------------------------------------------------------------------------------------|
//|VCounter |[1]0x00-0xE8(E*2)|[1]0x00-0xFE(E*2)| | |
//|progression |[2]0xEA(%E) |[2]0x01-0xFF(O*2)|<Same as H32V28> |<Same as H32V30> |
//|(external) |[3]0xE5-0xFF(O*2)| | | |
//| | | | | |
//|--------------------------------------------------------------------------------------|
//|VCounter |[1]0x00-0xEA(%E) |[1]0x00-0xFF(*T),| | |
//|progression |[2]0xE5-0xFF(*T) |[2]0x00-0xFF(*T) |<Same as H32V28> |<Same as H32V30> |
//|(internal) | | | | |
//| | | | | |
//|--------------------------------------------------------------------------------------|
//|ODD flag |Same time F flag |Same time F flag | | |
//|toggled |set, which is the|set, which is the| | |
//| |same as without |same as without | | |
//| |interlacing. |interlacing. | | |
//| |HCounter changes |HCounter changes |<Same as H32V28> |<Same as H32V30> |
//| |from 0x00 to 0x01|from 0x00 to 0x01| | |
//| |at start of [1], |at start of [1], | | |
//| |while VCounter is|while VCounter is| | |
//| |set to 0xE0 in |set to 0xF0 in | | |
//| |[1]. |[1]. | | |
//----------------------------------------------------------------------------------------
O*2 - Runs odd values in the progression only, and keeps the same value for two complete HCounter progression cycles.
*T - Toggles the internal 9th VCounter bit when the internal VCounter overflows from 0xFF to 0x00.
%E - Runs for one HCounter line only, in both odd and even frames. Also toggles the 9th vcounter bit like the *T state.
Results from double interlace mode:
Code: Select all
//----------------------------------------------------------------------------------------
//| Video |PAL |PAL |PAL |PAL |
//| Mode |H32 (RSx=00) |H32 (RSx=00) |H40 (RSx=11) |H40 (RSx=11) |
//| |V28 (M2=0) |V30 (M2=1) |V28 (M2=0) |V30 (M2=1) |
//| Test |Int dub (LSMx=11)|Int dub (LSMx=11)|Int dub (LSMx=11)|Int dub (LSMx=11)|
//|--------------------------------------------------------------------------------------|
//|VCounter |[1]0x00-0xFE(E) |[1]0x00-0xFE(E) | | |
//|progression |[2]0x01-0xFF(O) |[2]0x01-0xFF(O) | | |
//|(external) |[3]0x00-0x02(E) |[3]0x00-0x12(E) |<Same as H32V28> |<Same as H32V30> |
//| |[4]0x93(#ODD) |[4]0xA3(#ODD) | | |
//| |[5]0x95-0xFF(O) |[5]0xA5-0xFF(O) | | |
//|--------------------------------------------------------------------------------------|
//|VCounter |[1]0x00-0xFF |[1]0x00-0xFF | | |
//|progression |[2]0x00-0x01 |[2]0x00-0x09 |<Same as H32V28> |<Same as H32V30> |
//|(internal) |[3]0xC9(#ODD) |[3]0xD1(#ODD) | | |
//| |[4]0xCA-0xFF |[4]0xD2-0xFF | | |
//|--------------------------------------------------------------------------------------|
//|ODD flag |Same time F flag |Same time F flag | | |
//|toggled |set, which is the|set, which is the| | |
//| |same as without |same as without | | |
//| |interlacing. |interlacing. | | |
//| |HCounter changes |HCounter changes |<Same as H32V28> |<Same as H32V30> |
//| |from 0x00 to 0x01|from 0x00 to 0x01| | |
//| |at start of [1], |at start of [1], | | |
//| |while VCounter is|while VCounter is| | |
//| |set to 0xE0 in |set to 0xF0 in | | |
//| |[1]. |[1]. | | |
//----------------------------------------------------------------------------------------
O - Runs odd values in the progression only.
#ODD - Runs only when the ODD flag is set, and for one HCounter progression cycle only.
Code: Select all
//----------------------------------------------------------------------------------------
//| Video |NTSC |NTSC |NTSC |NTSC |
//| Mode |H32 (RSx=00) |H32 (RSx=00) |H40 (RSx=11) |H40 (RSx=11) |
//| |V28 (M2=0) |V30 (M2=1) |V28 (M2=0) |V30 (M2=1) |
//| Test |Int dub (LSMx=11)|Int dub (LSMx=11)|Int dub (LSMx=11)|Int dub (LSMx=11)|
//|--------------------------------------------------------------------------------------|
//|VCounter |[1]0x00-0xFE(E) |[1]0x00-0xFE(E) | | |
//|progression |[2]0x01-0xD5(O) |[2]0x01-0xFF(O) | | |
//|(external) |[3]0xC9(#ODD) |[3]0x00-0xFE(E) |<Same as H32V28> |<Same as H32V30> |
//| |[4]0xCB-0xFF(O) |[4]0x01-0xFF(O) | | |
//| | | | | |
//|--------------------------------------------------------------------------------------|
//|VCounter |[1]0x00-0xEA |[1]0x00-0xFF | | |
//|progression |[2]0xE4(#ODD) |[2]0x00-0xFF |<Same as H32V28> |<Same as H32V30> |
//|(internal) |[3]0xE5-0xFF | | | |
//| | | | | |
//|--------------------------------------------------------------------------------------|
//|ODD flag |Same time F flag |Same time F flag | | |
//|toggled |set, which is the|set, which is the| | |
//| |same as without |same as without | | |
//| |interlacing. |interlacing. | | |
//| |HCounter changes |HCounter changes |<Same as H32V28> |<Same as H32V30> |
//| |from 0x00 to 0x01|from 0x00 to 0x01| | |
//| |at start of [1], |at start of [1], | | |
//| |while VCounter is|while VCounter is| | |
//| |set to 0xE0 in |set to 0xF0 in | | |
//| |[1]. |[1]. | | |
//----------------------------------------------------------------------------------------
O - Runs odd values in the progression only.
#ODD - Runs only when the ODD flag is set, and for one HCounter progression cycle only.
General notes:
-If VINT is disabled by having bit 5 of VDP register 1 unset, the F flag is still set at the normal time, but it remains set from that point onward. In other words, if VINT is set as disabled, the F flag will be set the next time a VINT would be triggered, but it will not be cleared and will remain set from that point onward. The state of the HINT enable bit has no effect on this behaviour.
-Note that the values for H/V counter can actually change DURING a single read. Check sample "output - SR noint NTSCJ H40V30.bin", at 0x3FC. The VCounter was incremented from 0xED (11101101) to 0xEE(11101110) while it was being read, and actually came back as 0xEF(11101111). The following read came back as 0xEE as expected. Also check "output - SR noint NTSCJ H40V30 - 2.bin" at 0x669C, we have a case of the VCounter not being incremented at HCounter 0xA5. Also at 0xB228, increment from 0xC1 to 0xC2 coming up as 0xC3. Also check the HV counter reads for good cases.
-Testing long-word reads from the HV counter in H32 mode have shown that the hcounter always performs 1 to 2 update cycles between the back-to back word reads from the HV counter, IE, the H counter progresses by 1-2 steps between the two separate word components of the long-word read, and it's usually a 2 cycle delay. This means the HV counter will always be 1-2 steps ahead of the status register read we made when we do comparisons. The values I've given above take this delay into account, and I've compared results from multiple runs of the same test to ensure I've correctly compensated for the delay. Note that cases do appear where the H counter appears to not have progressed, but analysis has shown this is only due to the HV counter being modified during the read attempt causing a bogus read. Testing long-word reads from the HV counter in H40 mode have shown much the same thing as H32 mode, except that it really is almost always a 2 step lag now. There's no 3-step lag, but only very rarely will there be only a single step delay between back-to-back reads.
-Changes to the interlace mode are only latched, and therefore only have an effect on the status register/hv counter, when the VBlank set event occurs. As soon as the VCounter is incremented to the point where a VBlank set event occurs, the HV counter changes its current location and progression to adopt the interlace mode. The ODD flag will be toggled on the F flag set event immediately following the VBlank, IE, in just a few hcounter steps after the VBlank started.
-When interlace mode has just been enabled, the initial state of the ODD flag in the status register is cleared. It will be set as soon as the next update interval for the ODD flag is reached, which is just a few hcounter cycles after the updated interlace state is latched, as detailed above.
And here are the copies of the sampled data sets I used for most of my findings:
http://nemesis.hacking-cult.org/MegaDri ... esults.zip
This sampled data comes from a full dump of the Mega Drive system memory. I wrote a custom test rom which dumped results out to the first 0xF000 bytes of system ram, then used a modified version of the transfer code Mask of Destiny provided for his parallel transfer cable to get the data back off and onto my computer. Most samples come in "sets", numbered 1-9. The first file in the set comes as soon as the system is powered on. Sets 2-9 come from repeated dumps from the system after power-on. Note that when looking at the interlace testing in particular, the first part of the first file in the set won't have interlace enabled. The interlace mode kicks in partway through when the updated interlace register settings are sampled. Also note the delay between successive word reads that I elaborate on above. If you're examining the raw data, you'll need to take the delay into account when comparing the results.
Here's the compiled test roms I used to collect the data:
http://nemesis.hacking-cult.org/MegaDri ... 20roms.zip
Note that these were written to run on systems with no TMSS, so they won't work as-is on systems with TMSS. I didn't really keep the individual versions of the source I used to compile the test roms, but here's the latest version of the source file they were generated from:
http://nemesis.hacking-cult.org/MegaDri ... source.zip
Anyway, yeah. Feel free to ask any questions, or let me know if there are any other things you want me to test/clarify. I plan to repeat these tests with the invalid H-cell mode settings (RSx=10, RSx=01), and I'll post my findings here when I'm done.