hv counter details

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

Moderators: BigEvilCorporation, Mask of Destiny

Post Reply
fmahnke
Newbie
Posts: 1
Joined: Sun Jun 09, 2019 5:48 am

hv counter details

Post by fmahnke » Mon Jun 24, 2019 11:51 am

Hi, I've started on a Genesis/MD emulator and am having trouble understanding how the hv counter works across the pixels in a line. PAL H40 is what I'm trying to understand right now.

I've read the Sega documentation, which suggests the range of H is 0-9f and the result is shifted left 1 bit to get the pixel value. http://xi6.com/files/sega2f.html

This post on this board suggests ranges of 0x00-0xB6, 0xE4-0xFF. https://gendev.spritesmind.net/forum/vi ... .php?t=768

This wiki suggests something else entirely: http://md.railgun.works/index.php/VDP#H.2FV_Counter

I tried to sample it in the MAME debugger by running move.b $00c00009,d2 in a loop over an entire line and got results inconsistent with all the above:

Code: Select all

02,12,22,32,42,52,61,71,81,91,a1,b0,c0,d0,e0,f0,
b6,c6,d6,e6,f6,06,15,25,35,45,55,64,74,84,94,0d
I'm pretty confused and think I must be missing something fundamental. Could someone point me in the right direction?

For those who sample values directly from the hardware, what is the general approach? One way I can think of to do it is to run the move.b $00c00009,<ea> instruction in a loop on a real unit and sample the values off the 68k bus with an oscilloscope, but I bet that is naive.

Sik
Very interested
Posts: 890
Joined: Thu Apr 10, 2008 3:03 pm
Contact:

Re: hv counter details

Post by Sik » Mon Jun 24, 2019 2:33 pm

The H counter is the current pixel divided by 2. That means it'll be from 0 to 159 in the visible area (in H40). The counter keeps counting during blanking too, which is why you're seeing that discrepancy where "out of range" may show up (it's actually the pixels corresponding to those positions in the border area). I think Charles McDonald's docs have the correct ranges for the HV counter?

And yeah, the approach you suggest is naive — the counter may (will) change in the middle of the 68000 trying to read the value, so the bottom bits are likely to be garbage (as they'll flip in the middle of a read and likely show up as 1). A better approach is to do a range comparison (i.e. greater or equal) and to look for a value with enough significant bits that aren't bound to change in a way that makes the comparison true too early.
Sik is pronounced as "seek", not as "sick".

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

Re: hv counter details

Post by Nemesis » Thu Jun 27, 2019 12:12 pm

It's mostly covered by Sik, but I'll add a bit more detail on a few points. The H counter is simple enough, here are the notes on it from my Emulator:

Code: Select all

// The internal hcounter is 9-bit, and the external hcounter is 8-bit. The upper 8 bits
// of the 9-bit internal hcounter are used to build the 8-bit external hcounter, as
// shown below:
// Internal counter: 876543210
// External counter: 87654321
The V counter is actually quite a bit more complex. Here's its detail:

Code: Select all

// The internal vcounter is 9-bit, and the external vcounter is 8-bit. The way the
// internal counter is mapped to the external counter depends on the interlace mode.
// Given that the internal vcounter is 9-bit, the way the bits are mapped from the
// internal counter to the external counter is given below:
// Internal counter:           876543210
// External, no Interlacing:    76543210
// External, interlace normal:  76543218
// External, interlace double:  65432107
Regarding the reading of the HV counter, as Sik mentioned it does indeed get modified "live", meaning you can get what's called a "torn read" where only part of the data is updated. You don't get a case where the lower bits are effectively forced to "1" though, that's not what happens. What happens is that the 68000 "latches" each bit of the 16-bit value at almost the same time, but not exactly. The end result is, that you usually get a good read. Rarely though (from memory, maybe 1 time out of 100), you get a case where some of the bits (and it's basically random which bits) are from the previous value, and some of the bits are from the new value. This can result in the counter appearing to jump either forwards or backwards, as well as a few impossible values occurring. If you're relying on good reads from the HV counter, you need to be aware of this possibility, and try and deal with it. How easy that is to do will depend on how often you read the counter and what you're trying to use it for.

To go a step further and make this more complex than it probably needs to be, you also need to know how the HV counter values map to the actual positions in the HV scan process over the screen. For that, here's the data tables:

Code: Select all

// Analog screen sections in relation to HCounter (H32 mode):
// -----------------------------------------------------------------
// | Screen section | HCounter  |Pixel| Pixel |Serial|Serial |MCLK |
// | (PAL/NTSC H32) |  value    |clock| clock |clock |clock  |ticks|
// |                |           |ticks|divider|ticks |divider|     |
// |----------------|-----------|-----|-------|------|-------|-----|
// |Left border     |0x00B-0x017|  13 |SCLK/2 |   26 |MCLK/5 | 130 |
// |----------------|-----------|-----|-------|------|-------|-----|
// |Active display  |0x018-0x117| 256 |SCLK/2 |  512 |MCLK/5 |2560 |
// |----------------|-----------|-----|-------|------|-------|-----|
// |Right border    |0x118-0x125|  14 |SCLK/2 |   28 |MCLK/5 | 140 |
// |----------------|-----------|-----|-------|------|-------|-----|
// |Front porch     |0x126-0x127|   9 |SCLK/2 |   18 |MCLK/5 |  90 |
// |(Right Blanking)|0x1D2-0x1D8|     |       |      |       |     |
// |----------------|-----------|-----|-------|------|-------|-----|
// |Horizontal sync |0x1D9-0x1F2|  26 |SCLK/2 |   52 |MCLK/5 | 260 |
// |----------------|-----------|-----|-------|------|-------|-----|
// |Back porch      |0x1F3-0x00A|  24 |SCLK/2 |   48 |MCLK/5 | 240 |
// |(Left Blanking) |           |     |       |      |       |     |
// |----------------|-----------|-----|-------|------|-------|-----|
// |TOTALS          |           | 342 |       |  684 |       |3420 |
// -----------------------------------------------------------------

// Analog screen sections in relation to HCounter (H40 mode):
// --------------------------------------------------------------------
// | Screen section |   HCounter    |Pixel| Pixel |EDCLK| EDCLK |MCLK |
// | (PAL/NTSC H40) |    value      |clock| clock |ticks|divider|ticks|
// |                |               |ticks|divider|     |       |     |
// |----------------|---------------|-----|-------|-----|-------|-----|
// |Left border     |0x00D-0x019    |  13 |EDCLK/2|  26 |MCLK/4 | 104 |
// |----------------|---------------|-----|-------|-----|-------|-----|
// |Active display  |0x01A-0x159    | 320 |EDCLK/2| 640 |MCLK/4 |2560 |
// |----------------|---------------|-----|-------|-----|-------|-----|
// |Right border    |0x15A-0x167    |  14 |EDCLK/2|  28 |MCLK/4 | 112 |
// |----------------|---------------|-----|-------|-----|-------|-----|
// |Front porch     |0x168-0x16C    |   9 |EDCLK/2|  18 |MCLK/4 |  72 |
// |(Right Blanking)|0x1C9-0x1CC    |     |       |     |       |     |
// |----------------|---------------|-----|-------|-----|-------|-----|
// |Horizontal sync |0x1CD.0-0x1D4.5| 7.5 |EDCLK/2|  15 |MCLK/5 |  75 |
// |                |0x1D4.5-0x1D5.5|   1 |EDCLK/2|   2 |MCLK/4 |   8 |
// |                |0x1D5.5-0x1DC.0| 7.5 |EDCLK/2|  15 |MCLK/5 |  75 |
// |                |0x1DD.0        |   1 |EDCLK/2|   2 |MCLK/4 |   8 |
// |                |0x1DE.0-0x1E5.5| 7.5 |EDCLK/2|  15 |MCLK/5 |  75 |
// |                |0x1E5.5-0x1E6.5|   1 |EDCLK/2|   2 |MCLK/4 |   8 |
// |                |0x1E6.5-0x1EC.0| 6.5 |EDCLK/2|  13 |MCLK/5 |  65 |
// |                |===============|=====|=======|=====|=======|=====|
// |        Subtotal|0x1CD-0x1EC    | (32)|       | (64)|       |(314)|
// |----------------|---------------|-----|-------|-----|-------|-----|
// |Back porch      |0x1ED          |   1 |EDCLK/2|   2 |MCLK/5 |  10 |
// |(Left Blanking) |0x1EE-0x00C    |  31 |EDCLK/2|  62 |MCLK/4 | 248 |
// |                |===============|=====|=======|=====|=======|=====|
// |        Subtotal|0x1ED-0x00C    | (32)|       | (64)|       |(258)|
// |----------------|---------------|-----|-------|-----|-------|-----|
// |TOTALS          |               | 420 |       | 840 |       |3420 |
// --------------------------------------------------------------------

// Analog screen sections in relation to VCounter:
// -------------------------------------------------------------------------------------------
// |           Video |NTSC             |NTSC             |PAL              |PAL              |
// |            Mode |H32/H40(RSx00/11)|H32/H40(RSx00/11)|H32/H40(RSx00/11)|H32/H40(RSx00/11)|
// |                 |V28     (M2=0)   |V30     (M2=1)   |V28     (M2=0)   |V30     (M2=1)   |
// |                 |Int none(LSMx=*0)|Int none(LSMx=*0)|Int none(LSMx=*0)|Int none(LSMx=*0)|
// |                 |------------------------------------------------------------------------
// |                 | VCounter  |Line | VCounter  |Line | VCounter  |Line | VCounter  |Line |
// | Screen section  |  value    |count|  value    |count|  value    |count|  value    |count|
// |-----------------|-----------|-----|-----------|-----|-----------|-----|-----------|-----|
// |Active display   |0x000-0x0DF| 224 |0x000-0x1FF| 240*|0x000-0x0DF| 224 |0x000-0x0EF| 240 |
// |-----------------|-----------|-----|-----------|-----|-----------|-----|-----------|-----|
// |Bottom border    |0x0E0-0x0E7|   8 |           |   0 |0x0E0-0x0FF|  32 |0x0F0-0x107|  24 |
// |-----------------|-----------|-----|-----------|-----|-----------|-----|-----------|-----|
// |Bottom blanking  |0x0E8-0x0EA|   3 |           |   0 |0x100-0x102|   3 |0x108-0x10A|   3 |
// |-----------------|-----------|-----|-----------|-----|-----------|-----|-----------|-----|
// |Vertical sync    |0x1E5-0x1E7|   3 |           |   0 |0x1CA-0x1CC|   3 |0x1D2-0x1D4|   3 |
// |-----------------|-----------|-----|-----------|-----|-----------|-----|-----------|-----|
// |Top blanking     |0x1E8-0x1F4|  13 |           |   0 |0x1CD-0x1D9|  13 |0x1D5-0x1E1|  13 |
// |-----------------|-----------|-----|-----------|-----|-----------|-----|-----------|-----|
// |Top border       |0x1F5-0x1FF|  11 |           |   0 |0x1DA-0x1FF|  38 |0x1E2-0x1FF|  30 |
// |-----------------|-----------|-----|-----------|-----|-----------|-----|-----------|-----|
// |TOTALS           |           | 262 |           | 240*|           | 313 |           | 313 |
// -------------------------------------------------------------------------------------------
//*When V30 mode and NTSC mode are both active, no border, blanking, or retrace occurs. A
// 30-row display is setup and rendered, however, immediately following the end of the 30th
// row, the 1st row starts again. In addition, the VCounter is never reset, which usually
// happens at the beginning of vertical blanking. Instead, the VCounter continuously counts
// from 0x000-0x1FF, then wraps around back to 0x000 and begins again. Since there are only
// 240 lines output as part of the display, this means the actual line being rendered is
// desynchronized from the VCounter. Digital events such as vblank flags being set/cleared,
// VInt being triggered, the odd flag being toggled, and so forth, still occur at the
// correct VCounter positions they would occur in (IE, the same as PAL mode V30), however,
// since the VCounter has 512 lines per cycle, this means VInt is triggered at a slower
// rate than normal.
All this is confirmed through hardware testing.

Post Reply