Sega Mega Drive/VDP general usage

From Sega Retro

The VDP (Video Display Processor) is the main graphics processor in the Sega Mega Drive. It is capable of displaying two graphics planes - a foreground (plane A) and background (plane B) - which can be moved independently, as well as a static window plane, and up to 80 sprites (no more than 20 per scanline) on screen.

It contains 64kB of VRAM, used to store graphics data, nametable data for the three planes, horizontal scroll data for the foreground and background planes, and the sprite table. It also has 128 bytes of CRAM, used to store 64 colours (4 palettes of 16 colours). In practice, this equates to 61 usable colours since the first colour in each palette is transparent, and one of those is used as the "background" colour. Finally, VSRAM stores the vertical scroll data.

Ports

Features of the VDP such as VRAM are not directly accessible by the 68k. Instead, all instructions and data must be sent through the control and data ports.

68k Address Length Description
$C00000 B/W/L Data port
$C00002 B/W Data port (mirror)
$C00004 B/W/L Control port
$C00006 B/W Control port (mirror)
$C00008 B/W H/V counter (read-only)
$C0000A B/W H/V counter (read-only, mirror)
$C0000C B/W H/V counter (read-only, mirror)
$C0000E B/W H/V counter (read-only, mirror)
$C00011 B SN76489 PSG (write-only)
$C00013 B SN76489 PSG (write-only, mirror)
$C00015 B SN76489 PSG (write-only, mirror)
$C00017 B SN76489 PSG (write-only, mirror)
$C0001C W Debug register
$C0001E W Debug register (mirror)

Control port

Reading

Reading the control port always returns the status register, which contains information about what the VDP is currently doing.

move.w ($c00004).l,d0

Writing

Registers

Writing to the control port can modify one of 24 write-only registers, which are used to change various settings, or to set up a DMA transfer.

7
6
5
4
3
2
1
0

1
0
0
RS4-RS0
7
6
5
4
3
2
1
0

D7-D0
  • RS4-RS0: Register number.
  • D7-D0: Data to write to register.

To modify a register, write a word (1 byte for the register number, 1 byte for the modification) to the control port:

move.w #$8004,($c00004).l ; set register 0 to $04

Because the control port is mirrored at $c00006, it is possible to send two words at the same time:

move.l #$80048134,($C00004).l ; set register 0 to $04 and register 1 to $34

Addresses

The control port can also be used to point to an address in VRAM, CRAM or VSRAM, so that the 68k can send data to that address via the data port. This is done by writing a longword in the following format:

7
6
5
4
3
2
1
0

CD1
CD0
A13-A8
7
6
5
4
3
2
1
0

A7-A0
7
6
5
4
3
2
1
0

0
0
0
0
0
0
0
0
7
6
5
4
3
2
1
0

CD5
CD4
CD3
CD2
0
0
A15
A14
  • A15-A0: VRAM/CRAM/VSRAM address.
  • CD0: 1 = write; 0 = read.
  • CD3-CD1: 000 = VRAM; 100 = CRAM (read); 001 = CRAM (write); 010 = VSRAM; 110 = VRAM (byte read)
  • CD4: 1 = VRAM to VRAM copy.
  • CD5: 1 = DMA. Used to automate data transfer without the need for the data port.
Memory space Type CD3-CD0 Code
VRAM read 0000 move.l #$00000000+(($xxxx&$3FFF)<<16)+(($xxxx&$C000)>>14),($C00004).l
write 0001 move.l #$40000000+(($xxxx&$3FFF)<<16)+(($xxxx&$C000)>>14),($C00004).l
CRAM read 1000 move.l #$00000020+($xx<<16),($C00004).l
write 0011 move.l #$C0000000+($xx<<16),($C00004).l
VSRAM read 0100 move.l #$00000010+($xx<<16),($C00004).l
write 0101 move.l #$40000010+($xx<<16),($C00004).l

Data port

After an address has been set, data can be read or written at that location through the data port. If the auto-increment register has been set, the address will increase by that amount after every read/write. Attempts to read during a write operation or vice-versa will be ignored.

As an example, the following code swaps first four colours of the first palette line (CRAM addresses 0 to 7) with the first four colours of the last palette line (CRAM addresses $60 to $67):

	lea	($C00004).l,a5	; VDP control port
	lea	($C00000).l,a6	; VDP data port
	move.w	#$8F02,(a5)	; set auto-increment to 2 (see registers page for explanation)
	
	move.l	#$00000020,(a5)	; CRAM read at address $0
	move.l	(a6),d0		; get first two colours of first palette line
	move.l	(a6),d1		; get next two colours of first palette line
	move.l	#$00600020,(a5)	; CRAM read at address $60
	move.l	(a6),d2		; get first two colours of last palette line
	move.l	(a6),d3		; get next two colours of last palette line
	
	move.l	#$C0000000,(a5)	; CRAM write at address $0
	move.l	d2,(a6)		; set first two colours of first palette line
	move.l	d3,(a6)		; set next two colours of first palette line
	move.l	#$C0600000,(a5)	; CRAM write at address $60
	move.l	d0,(a6)		; set first two colours of last palette line
	move.l	d1,(a6)		; set next two colours of last palette line

H/V counter

The H/V counter contains the current position of the electron beam on the screen. It consists of two bytes:

7
6
5
4
3
2
1
0

V7-V0
7
6
5
4
3
2
1
0

H8-H1
  • V7-V0: Vertical position.
  • H8-H1: Horizontal position. Lowest bit is omitted, so the value appears to be halved.

In interlace double resolution mode, the screen has twice as many scanlines and so the H/V counter is formatted differently:

7
6
5
4
3
2
1
0

V7-V1
V8
7
6
5
4
3
2
1
0

H8-H1
  • V8-V1: Vertical position. Lowest bit is omitted, so the value appears to be halved.
  • H8-H1: Horizontal position. Lowest bit is omitted, so the value appears to be halved.

Debug register

The debug register has various effects on VDP operation. Its features are undocumented and not known to be used by any games.

7
6
5
4
3
2
1
0

?
LSB
CH
PV
L1
7
6
5
4
3
2
1
0

L0
D
?
  • LSB: Locks the sprite buffer, resulting in some or all sprites becoming invisible.
  • PV: 1 = set PSG volume to that of channel CH.
  • CH: 00 = square 1; 01 = square 2; 10 = square 3; 11 = noise.
  • L0-L1: Force layer visible: 00 = off; 01 = sprites; 10 = plane A; 11 = plane B. (Overrides bit D).
  • D: 1 = disable display. Overrides VDP register 01.