VDP HV counter/Status register progression and timings

For anything related to VDP (plane, color, sprite, tiles)

Moderators: BigEvilCorporation, Mask of Destiny

Post Reply
Nemesis
Very interested
Posts: 791
Joined: Wed Nov 07, 2007 1:09 am
Location: Sydney, Australia

VDP HV counter/Status register progression and timings

Post by Nemesis » Mon Jul 26, 2010 5:19 am

I've noticed there seems to be a great lack of complete, accurate information on how the H/V counter progresses in all the various video modes the VDP offers, and when various status register flags are modified in relation to the H/V counter. I've just done extensive testing of this on the hardware, and I thought some of you guys might be interested in my findings. I've sampled quite a few MB's of data directly from the hardware in order to compile this information, and since I believe in providing as much info as possible, I'll be posting the datasets too so anyone can verify or re-check my findings.

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].             |                 |                 |
//----------------------------------------------------------------------------------------
Notes on interlace mode:
-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].             |                 |                 |
//----------------------------------------------------------------------------------------
E*2 - Runs even values in the progression only, and keeps the same value for two complete HCounter progression cycles.
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].             |                 |                 |
//----------------------------------------------------------------------------------------
E*2 - Runs even values in the progression only, and keeps the same value for two complete HCounter progression cycles.
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].             |                 |                 |
//----------------------------------------------------------------------------------------
E - Runs even values in the progression only.
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].             |                 |                 |
//----------------------------------------------------------------------------------------
E - Runs even values in the progression only.
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.

Eke
Very interested
Posts: 884
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Mon Jul 26, 2010 6:40 am

Excellent, thanks for having taken the time to do this, it pretty much matches the timing I use so far.

Only thing left would be exact hint/vint occurence in term of HCounter values (as already stated, I think the best way to do this would be to wire 68k IPL2 line to TH input and use the HVC latch property). Most likely, hint & vint respectively occur at the same time as VCounter increment & F flag occurence but this need confirmation.

Regarding HCounter in H40 mode, I noticed that if one value counts for 2 pixels, $00-B6:$E4-FF would actually make 422 pixels when we know there are only 420. This mean the internal counter probably runs like this (it is 9-bits and HVC returns the 8 high bits):
0($00),1($00)...,362($B5),363($B5),364($B6),-55($E4),-54($E5),-53($E5),...,-2($FF),-1($FF)

The description of both interlaced modes is also pretty much complete, so far the 2 things that were still unknown are covered:

(1) VCounter values in interlaced mode 1 (the use of the 9th bit). It seems like it's also the sign bit in the case of NTSC (where there is no overflow from 0xFF to 0x100). In fact, both modes pretty do the same, except that, in interlaced mode 2, the 9-bits internal value is first shifted to the left , so the 9th bit is lost and the lsb is actually replaced by the former 8th bit instead.

(2) There is one line less (312) on even frames in both PAL interlaced modes and there is one line more (263) on odd frames in NTSC interlaced mode 2 only (are you sure it's not the case in both modes ?)
Last edited by Eke on Mon Jul 26, 2010 2:10 pm, edited 4 times in total.

mickagame
Very interested
Posts: 256
Joined: Sat Jun 07, 2008 7:37 am

Post by mickagame » Mon Jul 26, 2010 11:14 am

Thanks for sharing all this precious infos !!

Nemesis
Very interested
Posts: 791
Joined: Wed Nov 07, 2007 1:09 am
Location: Sydney, Australia

Post by Nemesis » Mon Jul 26, 2010 11:41 pm

Regarding HCounter in H40 mode, I noticed that if one value counts for 2 pixels, $00-B6:$E4-FF would actually make 422 pixels when we know there are only 420. This mean the internal counter probably runs like this (it is 9-bits and HVC returns the 8 high bits):
0($00),1($00)...,362($B5),363($B5),364($B6),-55($E4),-54($E5),-53($E5),...,-2($FF),-1($FF)
Good work. I've just confirmed your suspicions, this is correct. I wrote a program to combine and sort these results in various ways, which I used to perform my analysis on this data originally. Sorting just by hcounter values for a H40 set, I found that the HCounter values 0xB6 and 0xE4 occur exactly half as often as the surrounding hcounter values. I sampled 2584 cases where the hcounter was 0xB5 for example, but only 1316 cases where it was 0xB6, 1364 cases where it was 0xE4, and then 2636 cases where it was 0xE5, meaning it happens either half as often, or stays set for half as long. Half as long makes sence given what you posted, so it's fair to say the hcounter is 9-bit internally, and progresses as you showed. That also makes a lot of sence, seeing as I know from testing that the vcounter is definitely 9-bit internally too.

I'll amend the original post with your updated findings later on today. I'll also look at the other comments you made, just short on time right now.

Eke
Very interested
Posts: 884
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Tue Jul 27, 2010 8:13 am

Another note: it seems like the VCounter roll back is coinciding with the end of bottom blanking / start of vsync.

Here are the values measured by Charles MacDonald:
NTSC, 256x192
-------------

Lines Description

192 Active display
24 Bottom border
3 Bottom blanking
3 Vertical blanking
13 Top blanking
27 Top border

V counter values
00-DA, D5-FF

NTSC, 256x224
-------------

Lines Description

224 Active display
8 Bottom border
3 Bottom blanking
3 Vertical blanking
13 Top blanking
11 Top border

V counter values
00-EA, E5-FF

NTSC, 256x240
-------------

This mode does not work on NTSC machines. All 30 rows of the name table are
displayed, there is no border, blanking, or retrace period, and the next
frame starts after the 30th row. The display rolls continuously though it
can be stabilized by adjusting the vertical hold.

V counter values
00-FF, 00-06

PAL, 256x192
------------

Lines Description

192 Active display
48 Bottom border
3 Bottom blanking
3 Vertical blanking
13 Top blanking
54 Top border

V counter values
00-F2, BA-FF

PAL, 256x224
------------

Lines Description

224 Active display
32 Bottom border
3 Bottom blanking
3 Vertical blanking
13 Top blanking
38 Top border

V counter values
00-FF, 00-02, CA-FF

PAL, 256x240
------------

Lines Description

240 Active display
24 Bottom border
3 Bottom blanking
3 Vertical blanking
13 Top blanking
30 Top border

V counter values
00-FF, 00-0A, D2-FF

Here are some details about what the different screen areas look like,
useful if you are emulating overscan or if you want to have a 'virtual'
vertical hold control in your emulator.

Active display - Where the display generated by the VDP goes.
Bottom border - Filled with border color from VDP register #7.
Bottom blanking - Filled with a light black color. (like display was blanked)
Vertical sync - Filled with a pure black color. (like display was turned off)
Top blanking - Filled with a light black color. (like display was blanked)
Top border - Filled with the border color from VDP register #7.
in case of NTSC H28 for example, active screen + bottom blanking is 224 +8 + 3 = 235 (lines 0-234).
Line 234 would be 234 = $0EA
Line 235 would be 235-262=-27=$1E5

same things works in all other modes, which makes vcounter implementation quite logical

Nemesis
Very interested
Posts: 791
Joined: Wed Nov 07, 2007 1:09 am
Location: Sydney, Australia

Post by Nemesis » Tue Jul 27, 2010 11:12 am

(2) There is one line less (312) on even frames in both PAL interlaced modes and there is one line more (263) on odd frames in NTSC interlaced mode 2 only (are you sure it's not the case in both modes ?)
Thanks, you're right, there is one more line in odd frames in both NTSC interlace modes. Just like in the double interlace mode, the internal vcounter runs a line at 0xE4 in odd frames only, I just forgot to take into account that the lowest bit of the vcounter was being discarded on the output in that interlace mode when looking for an extra line. I can see that the external vcounter runs two lines at a value of 0xE4 in normal interlace mode in an odd frame, and only one line in an even frame, so that means that the internal vcounter runs an extra line at 0xE4 for odd frames only.

Post Reply