MD VDP Research thread

Jorge Nuno

Member
Messages
2
First post lol.

Over the course of the years breaki-- researching megadrives I've been gathering info from various sources and also discovering small bits here and there on my own. This thread serves as a means to document these findings in particular about the diplay processor somewhere more proper.

So the VDP has 3 memories attached to it accessible from the CPU: VRAM, CRAM and VSRAM. VRAM is external and the others are inside the chip but from the CPU interface they are treated in the same way and are all subjected to the FIFO (slot access delays, refresh and so on). To access them we need to send a 32 bit command word that tells it where does the CPU want to access with the already known format:

CD1 CD0 A13 A12 A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00 - - - - - - - - CD5 CD4 CD3 CD2 - A16 A15 A14

CD3-0 are the target selector bits, CD0 acting as a read/write selector in most cases*
CD5 is the DMA start flag and CD4 is used for VRAM to VRAM DMA copy.
0000b : VRAM read
0001b : VRAM write
0010b : Register write
0011b : CRAM write
0100b : VSRAM read
0101b : VSRAM write
0110b : Unreachable via normal means, unknown (maybe register write)
0111b : Unreachable via normal means, unknown target
1000b : CRAM read
1001b : Nothing
1010b : Unreachable via normal means, unknown (maybe register write)
1011b : Unreachable via normal means, unknown target
1100b : 8-bit VRAM half read
1101b : Nothing
1110b : Unreachable via normal means, unknown (maybe register write)
1111b : Unreachable via normal means, unknown target


Reachable invalid codes either do 8-bit VRAM half reads or send data ports writes to nowhere. Setting DMA transfers with invalid codes (CD5 = 1) will still trigger the DMA operation, simply the data doesn't end up anywhere. Writing to the data port is always allowed regardless if the VDP has been configured or not. Reading is different, when the VDP is setup to read something it will attempt to read that location, then places the data on the read buffer and then it allows the 68k to read it. When there is no valid read target the 68k will be stalled forever. This is imporant because it's not possible to read-modify-right the VDP memories so easily without changing the command word as well. Also because of a quirk of the 68k, the clr instruction (does a useless read cycle) when used on the VDP data port will freeze the system.

Now because the 68k has a 16bit bus the write operation of the command is in reality a 2 step process, so when the CPU encounters a move.l #$C0000000, $C00004 to setup a CRAM write on address 0 what actually happens is that the 68k writes the first half write of C000 to C00004, prefetches the next opcode and then finally writes the second half of 0000 to C00006. The VDP implements mirrors of the data and control (actually all) ports so that it becomes transparent and it also implements a "command pending" flag to keep track of where which half is meant to go, this can cause some problems when doing split word operations. Of course because the port at C00006 is a true mirror we can send both halves of the command to this address, or both to C00004 and it gets aligned properly because the of that pending flag. Only the order is important. Some documents state that reading back the control port will reset this pending flag, which is useful in case of a reset. What also resets the pending flag is any VDP register write which is important to keep in mind when implementing art/DMA queues so make sure to configure the registers first and only send the command after. DMA copies always seem to operate on VRAM even wth other target selections. DMA fills with other targets will probably work but I haven't tested this yet. It's possible to mangle VDP registers by changing the CD bits while the DMA fill operation is in progress which is why there is a "DMA pending" status flag.

In SMS mode, CD5-2 will never be able to be accessed because the pending flag will stay at 0. This is why a Register write occupies the "slot" of what would presumably be a CRAM read operation which ended up being relocated to a different code.

When a DMA transfer command is configured, the VDP will start the transfer immediately after the write cycle.

And now the VDP register bits. These are based of previous findings by Charles MacDonald, Nemesis, Tiido Priimagi, Nukeykt, maybe others and also my own research. I'm emphasizing more the "uncommon bits" than the usual ones because those are already are explained in other places. Bit abbreviations come from the official documentation.

Register 0 "Mode Set Register #1":
Bit 0: !!! Should be set to 0 normally - Enables locking onto external sync via the CSync pin, no display without a valid sync pulse source;
Bit 1: M3 HV counter latch enable;
Bit 2: !!! Should be set to 1 normally - Enables full palette bit depth as opposed to just the LSBs;
Bit 3: !!! Should be set to 0 normally - Outputs VCounter(0) on HSync pin when set, messes up EDCLK divisor ratios, RS0/1 = 11 H40 video mode will get thrashed;
Bit 4: IE1 (Horizontal interrupt enable) - Will trigger an interrupt immediately when set because they occur in the background anyway;
Bit 5: LCB (Leftmost column blank), only covers the leftmost 8-pixel column - leftover from SMS;
Bit 6: Unused or unknown effect, Former SMS horizontal scroll lock;
Bit 7: Forces columns 24-31 (but not 32-39 in H40 mode) to a vertical scroll of 0 - Former SMS vertical lock;

Register 1 "Mode Set Register #2":
Bit 0: Unused or unknown effect - Charles mentions that setting this bit causes the scroll values to shift the sync pulse positions, former SMS Sprite MAG;
Bit 1: Unused or unknown effect - Former SMS Sprite Size selection;
Bit 2: SMS / Genesis display select;
Bit 3: M2 (Enable V30 - PAL Only) - setting this bit in NTSC mode causes loss of Vertical sync and level 4 interrupts trigger once per 2 frames;
Bit 4: M1 (DMA Enable);
Bit 5: IE0 (Vertical Interrupt Enable) - Similar to IE1, it will trigger an interrupt immediately when set because they occur in the background;
Bit 6: DISP (Display Enable) - Only the BG color is drawn in normal intensity when this bit is cleared;
Bit 7: !!! Should be set to 0 normally - Enables 128kB VRAM;

Register 2 "Nametable Address for Plane A":
Bit 0: Unused or unknown effect - Saw this marked as "?" somewhere;
Bit 1: Unused or unknown effect;
Bit 2: Unused or unknown effect;
Bit 3: Bit 13 Nametable Address;
Bit 4: Bit 14 Nametable Address;
Bit 5: Bit 15 Nametable Address;
Bit 6: Bit 16 Nametable Address - Only significant when 128kB VRAM is enabled;
Bit 7: Unused or unknown effect;

Register 3: "Nametable Address for Window":
Bit 0: Unused or unknown effect;
Bit 1: Bit 11 Nametable Address - Irrelevant in H40 mode;
Bit 2: Bit 12 Nametable Address;
Bit 3: Bit 13 Nametable Address;
Bit 4: Bit 14 Nametable Address;
Bit 5: Bit 15 Nametable Address;
Bit 6: Bit 16 Nametable Address - Only significant when 128kB VRAM is enabled;
Bit 7: Unused or unknown effect;

Register 4: "Nametable Address for Plane B":
Bit 0: Bit 13 Pattern Nametable Address;
Bit 1: Bit 14 Pattern Nametable Address;
Bit 2: Bit 15 Pattern Nametable Address;
Bit 3: Bit 16 Pattern Nametable Address - Only significant when 128kB VRAM is enabled;
Bit 4: Unused or unknown effect;
Bit 5: Unused or unknown effect;
Bit 6: Unused or unknown effect;
Bit 7: Unused or unknown effect;

Register 5: "Sprite Attribute Table Address":
Bit 0: Bit 09 Attribute Table Address - Irrelevant in H40 mode;
Bit 1: Bit 10 Attribute Table Address;
Bit 2: Bit 11 Attribute Table Address;
Bit 3: Bit 12 Attribute Table Address;
Bit 4: Bit 13 Attribute Table Address;
Bit 5: Bit 14 Attribute Table Address;
Bit 6: Bit 15 Attribute Table Address;
Bit 7: Bit 16 Attribute Table Address - Only significant when 128kB VRAM is enabled;

Register 6: "Sprite Pattern Generator Address":
Bit 0: Unused or unknown effect;
Bit 1: Unused or unknown effect;
Bit 2: Unused or unknown effect;
Bit 3: Unused or unknown effect;
Bit 4: Unused or unknown effect;
Bit 5: Bit 16 Sprite Pattern Base Address - Used to reposition the sprite tile data to the upper half of VRAM when 128kB VRAM is enabled;
Bit 6: Unused or unknown effect;
Bit 7: Unused or unknown effect;

Register 7: "Background Color":
Bit 0: Bit 0 Color Index;
Bit 1: Bit 1 Color Index;
Bit 2: Bit 2 Color Index;
Bit 3: Bit 3 Color Index;
Bit 4: Bit 0 Palette Index;
Bit 5: Bit 1 Palette Index;
Bit 6: Unknown: Shadows background color when disabling drawing via C0001C register 000 bit 6;
Bit 7: Unknown: Same as bit 6;

Register 8: "??" (Former SMS Horizontal scroll offset):
Bit 0: Unused or unknown effect;
Bit 1: Unused or unknown effect;
Bit 2: Unused or unknown effect;
Bit 3: Unused or unknown effect;
Bit 4: Unused or unknown effect;
Bit 5: Unused or unknown effect;
Bit 6: Unused or unknown effect;
Bit 7: Unused or unknown effect;

Register 9: "??" (Former SMS Vertical scroll offset):
Bit 0: Unused or unknown effect;
Bit 1: Unused or unknown effect;
Bit 2: Unused or unknown effect;
Bit 3: Unused or unknown effect;
Bit 4: Unused or unknown effect;
Bit 5: Unused or unknown effect;
Bit 6: Unused or unknown effect;
Bit 7: Unused or unknown effect;

Register A: "Horizontal Interrupt Period Register":
Bit 0: Counter Bit 0;
Bit 1: Counter Bit 1;
Bit 2: Counter Bit 2;
Bit 3: Counter Bit 3;
Bit 4: Counter Bit 4;
Bit 5: Counter Bit 5;
Bit 6: Counter Bit 6;
Bit 7: Counter Bit 7;

Register B: "Mode Set Register #3":
Bit 0: LSCR (Horizontal scroll mode);
Bit 1: HSCR (Horizontal scroll mode);
Bit 2: VSCR (2-Column based Vertical scrolling);
Bit 3: IE2 (enable external interrupts);
Bit 4: Unused or unknown effect;
Bit 5: Unused or unknown effect;
Bit 6: !!! Should be set to 0 normally - Enables RAS0/CAS0 work RAM signals for Dynamic RAM - locks system;
Bit 7: Selects 68k address muxed for DRAM operation when cleared or Color code output when set - no effect on a normal system

Register C: "Mode Set Register #4":
Bit 0: RS1 - Select Horizontal cell mode and internal dot clock divisor);
Bit 1: LSM0 (Interlace mode setting);
Bit 2: LSM1 (Interlace mode setting);
Bit 3: S/TE (Shadow & Highlight);
Bit 4: Enable sprite/plane indicator (SPA/B pin) output, changes to input when cleared: sets background pin YS to 0 when SPA/B is 0), on the system this line is pulled to 5V with a 2.2k resistor so it always reads as a 1;
Bit 5: Disable HSync pin / Change HSync to input - messes up EDCLK (becomes constant 13.3 MHz), also used by the 32X; Maybe it could be used for video resyncronization although it's not clear how.
Bit 6: Outputs pixel clock on VSync pin when set, otherwise it's the VSync pulse - used by the 32X;
Bit 7: RS0 - Selects External Dot Clock (EDCLK pin) - used for "normal" H40;

Register D: "Horizontal Scroll Table Base Address":
Bit 0: Bit 10 Table Base Address;
Bit 1: Bit 11 Table Base Address;
Bit 2: Bit 12 Table Base Address;
Bit 3: Bit 13 Table Base Address;
Bit 4: Bit 14 Table Base Address;
Bit 5: Bit 15 Table Base Address;
Bit 6: Bit 16 Table Base Address - Only significant when 128kB VRAM is enabled;
Bit 7: Unused or unknown effect;

Register E: "Pattern Generator Base Address":
Bit 0: "Bit 16 Scroll A Pattern Generator Base Address - Used to reposition the tile data to the upper half of VRAM when 128kB VRAM is enabled;
Bit 1: Unused or unknown effect;
Bit 2: Unused or unknown effect;
Bit 3: Unused or unknown effect;
Bit 4: "Bit 16 Scroll B Pattern Generator Base Address - Used to reposition the tile data to the upper half of VRAM when 128kB VRAM is enabled;
Bit 5: Unused or unknown effect;
Bit 6: Unused or unknown effect;
Bit 7: Unused or unknown effect;

Register F: "VDP Auto Increment Amount":
Bit 0: Bit 0 Increment Amount;
Bit 1: Bit 1 Increment Amount;
Bit 2: Bit 2 Increment Amount;
Bit 3: Bit 3 Increment Amount;
Bit 4: Bit 4 Increment Amount;
Bit 5: Bit 5 Increment Amount;
Bit 6: Bit 6 Increment Amount;
Bit 7: Bit 7 Increment Amount;

Register 10: "Nametable Size":
Bit 0: HSZ0 (Horizontal Nametable size);
Bit 1: HSZ1 (Horizontal Nametable size) - When set alone it only draws the first row, repeated over all the screen;
Bit 2: Unused or unknown effect;
Bit 3: Unused or unknown effect;
Bit 4: VSZ0 (Vertical Nametable size);
Bit 5: VSZ1 (Vertical Nametable size) - When set alone it reads the nametable without the second MSB address as if the V128 nametable had a pair of "holes" 32 tiles high;
Bit 6: Unused or unknown effect;
Bit 7: Unused or unknown effect;

Register 11: "Window Horizontal Position":
Bit 7: RIGT;
Bit 6: Unused or unknown effect;
Bit 5: Unused or unknown effect;
Bit 4: WHP5;
Bit 3: WHP4;
Bit 2: WHP3;
Bit 1: WHP2;
Bit 0: WHP1;

Register 12: "Window Vertical Position":
Bit 7: DOWN;
Bit 6: Unused or unknown effect;
Bit 5: Unused or unknown effect;
Bit 4: WHP4;
Bit 3: WHP3;
Bit 2: WHP2;
Bit 1: WHP1;
Bit 0: WHP0;

Register 13: "DMA Length Counter Low" (Decrements by 1 as the DMA progresses):
Bit 0: Bit 0 Transfer Amount;
Bit 1: Bit 1 Transfer Amount;
Bit 2: Bit 2 Transfer Amount;
Bit 3: Bit 3 Transfer Amount;
Bit 4: Bit 4 Transfer Amount;
Bit 5: Bit 5 Transfer Amount;
Bit 6: Bit 6 Transfer Amount;
Bit 7: Bit 7 Transfer Amount;

Register 14: "DMA Length Counter High" (Decrements as the DMA progresses):
Bit 0: Bit 8 Transfer Amount;
Bit 1: Bit 9 Transfer Amount;
Bit 2: Bit 10 Transfer Amount;
Bit 3: Bit 11 Transfer Amount;
Bit 4: Bit 12 Transfer Amount;
Bit 5: Bit 13 Transfer Amount;
Bit 6: Bit 14 Transfer Amount;
Bit 7: Bit 15 Transfer Amount;

Register 15: "DMA Source Address - Low Byte" (Increments by 1 as the DMA progresses):
Bit 0: Source Address bit 1;
Bit 1: Source Address bit 2;
Bit 2: Source Address bit 3;
Bit 3: Source Address bit 4;
Bit 4: Source Address bit 5;
Bit 5: Source Address bit 6;
Bit 6: Source Address bit 7;
Bit 7: Source Address bit 8;

Register 16: "DMA Source Address - Middle Byte" (Increments by 1 when Reg 15 has carry as the DMA progresses):
Bit 0: Source Address bit 9;
Bit 1: Source Address bit 10;
Bit 2: Source Address bit 11;
Bit 3: Source Address bit 12;
Bit 4: Source Address bit 13;
Bit 5: Source Address bit 14;
Bit 6: Source Address bit 15;
Bit 7: Source Address bit 16;

Register 17: "DMA Source Address - High Byte" (Does not increment as the DMA progresses):
Bit 0: Source Address bit 17;
Bit 1: Source Address bit 18;
Bit 2: Source Address bit 19;
Bit 3: Source Address bit 20;
Bit 4: Source Address bit 21;
Bit 5: Source Address bit 22;
Bit 6: DMD0 (DMA Copy Mode or Address bit 23 in case of a 68k -> VDP transfer);
Bit 7: DMD1 (DMA Fill/Copy Mode when set, DMA Transfer when cleared);


Writing to registers 0x18 onwards doesn't seem to cause any effect, it seems that the VDP will just send the data nowhere.

On powerup all register bits are set to 0. CRAM and VSRAM will also have 0, although VRAM will have junk in it (I've seen patterns in the form of AA55 / 55AA sometimes with bit noise). Various flash carts may leave around some leftovers from their menus and such (and also the TMSS rom).

The ports at C00018 / 1A and C0001C / 1E were probably for internal testing of the VDP and are not described anywhere in the official documentation. To my knowledge, Overdrive 2 released 2017 was the first production to rely on them to acheive some visual effects. Emulator support is very limited.

port C00018 / C0001A: "Debug register offset‌ index"
Bit 0: Unused or unknown effect;
Bit 1: Unused or unknown effect;
Bit 2: Unused or unknown effect;
Bit 3: Unused or unknown effect;
Bit 4: Unused or unknown effect;
Bit 5: Unused or unknown effect;
Bit 6: Unused or unknown effect;
Bit 7: Unused or unknown effect;
Bit 8: Debug register selection;
Bit 9: Debug register selection;
Bit10: Debug register selection;
Bit11: Debug register selection;
Bit12: Unused or unknown effect;
Bit13: Unused or unknown effect;
Bit14: Unused or unknown effect;
Bit15: Unused or unknown effect;


port C0001C / C0001E: register 0: reading returns high-z
Bit 0: Unused or unknown effect;
Bit 1: Unknown: Appears to cause a infinite CRAM write cycle under certain circunstances. Unrecoverable, needs power cycle
Bit 2: Unused or unknown effect;
Bit 3: Unknown: Appears to cause a infinite CRAM write cycle under certain circunstances. Unrecoverable, needs power cycle
Bit 4: Unused or unknown effect;
Bit 5: Unknown: Appears to change VRAM interface when set (maybe non multiplexed SRAM-based?). Displays junk in a normal system because it only accesses up to 256 (scattered) locations
Bit 6: Disables display and shows background color in normal or shadowed intensity, takes priority over register 1 - bit 6
Bit 7: Forces layers to be always active (early systems will apply wire-OR to the color output and later systems wire-AND. Early systems will also corrupt the CRAM over time to a varying degree if the bits stay set)
Bit 8: Forces layers to be always active (in combination with bit 7: 01 = sprites always visible, 10: plane A always visible, 11: plane B always visible)
Bit 9: Replaces PSG overall output volume with channel volume when set
Bit10: PSG channel select
Bit11: PSG channel select (11 = noise channel)
Bit12: Unknown: Appears to freeze some sort of sprite internal process resulting in garbage
Bit13: Unknown: Appears to freeze some sort of sprite internal process
Bit14: Unknown: Appears to freeze some sort of sprite internal process
Bit15: Unused or unknown effect;


register 100: reading returns high-z
Bit 0: Caution: Selects Z80 clock, 3.57 MHz when cleared or 7.6 MHz when set. Overclocks the CPU above its spec, may produce a runt pulse when changing the setting. The RAM is able to keep up with the higher speed but the CPU may not on a case by case basis. Speeds up PSG clock as well
Bit 1: Hazard: EDCLK pin direction, conflicts with external on board driver when set (RS0/1 = 11 H40 video mode will get thrashed, may also stress the VDP or the divider IC pin in a normal system)
Bit 2: Caution: Causes the VCounter to increment every pixel, destroys the sync pulse regardless of video mode
Bit 3: Unused or unknown effect;
Bit 4: (??) Moving junk pattern
Bit 5: (??) Moving junk pattern
Bit 6: (??) Moving junk pattern
Bit 7: (??) Apppears to make the hscroll table be read from address 0
Bit 8: (??)
Bit 9: (??)
Bit10: (??)
Bit11: Unused or unknown effect;
Bit12: Unused or unknown effect;
Bit13: Unused or unknown effect;
Bit14: Unused or unknown effect;
Bit15: Unused or unknown effect;


register 200: reading returns a walking 0 bit pattern in the lower byte

registers 300 - e00: unknown

register f00: VDP reset

I have some additional info scattered around regarding the VDP status word, access delays, DMA bus operations, HV counter progression, etc that I'll try to collect.
Questions comments are welcome. Requests or ideas what to look for when testing unknown stuff as well, I have some ability to bend the VDP or the 68k to my will in certain ways but sometimes things are not noticeable. Maybe some better formatting or some images even would be nice too.

That's all.
 
Last edited:
Today I did a (re)capture of the HV counter values along the 223rd raster line and another one of the VDP status port in H40 video mode and then aligned them manually. This was made using a fake 68000 processor to perform an extremely long read of both C00004 and C00008.

The horizontal counter starts at 00 and increments by 1 every 2 pixels* (with exception on a special position) and it's the same between PAL and NTSC.
When it reaches the value of 05 it clears the horizontal blanking flag.
When it reaches the value of 06 (in the middle, so count value like "6.5") it starts drawing the border.
When it reaches the value of 0d the border ends and the active screen starts.
When it reaches the value of a5 it increments VCounter by 1 (it also sets the Vertical blanking flag at this point because it reached line 224). It may also generate a horizontal interrupt at this position if it is enabled.
When it reaches the value of ad it starts drawing the border again (active screen ends).
When it reaches the value of b3 it sets the horizontal blanking flag once again.
When it reaches the value of b4 the right border ends.
When it reaches the value of b6 it jumps to the value of e4. (both of these values only occur for a single pixel unlike all others).
When it reaches the value of e6 (about in the middle) the HSync pulse goes low and the EDClk divisor changes (but this plays no role in the counter behavior, just that the pixels are now stretched longer than normally).
When it reaches the value of f6 (about in the middle) the HSync pulse becomes high and the pixels are now "square" again.
When it reaches the value of 00 again (E000) the Vertical interrupt gets triggered if it's enabled and it also sets the Vertical interrupt pending status (bit 7) and the process repeats.

The edited capture is here, you can get a java program to view it https://www.timing-diagrams.com/download.html, or you can write your own parser, the data is text based and easy to recognize.

In the "Fast H40" video mode the values and trip points are the same just that the line period is only 3360 MClks instead of 3420 because the divider is always 8.

In H32 mode the counter behaves slighly different but I'll do this later.
 
Last edited:
Back
Top