This article is a work in progress.
Its content and location may change completely and frequently until this notice is removed.

The main Mega Drive processor (the Motorola 68000) cannot directly access VDP memory areas. Instead, it communicates with the VDP through ports.

Memory map

The addresses for the VDP ports are shown exhaustively below, though only $C00000 and $C00004 are used generally.

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

HV counter (read-only)

$C0000A

B/W

HV counter (read-only, mirror)

$C0000C

B/W

HV counter (read-only, mirror)

$C0000E

B/W

HV 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

Unused mystery port

Control port

The control port, as the name suggests, allows the 68000 to control VDP memory access. Additionally, it also allows the reading of VDP status and the setting of registers. The control port is accessed at address $C00004 from the 68000.

Getting VDP status

Reading the control port returns a VDP status word, which has the following format, counting from the least significant bit:

Bit

Description

0

This is 0 if the VDP is in NTSC mode, and 1 if it's in PAL mode.

1

This is set for the duration of a DMA operation. It is only useful for fills and copies, as the 68000 is frozen for transfers from 68000 memory to VDP.

2

Set during horizontal blanking.

3

Set during vertical blanking.

4

Set when the odd frame is being displayed in interlaced mode, clear otherwise.

5

Set when any two sprites have non-transparent pixels overlapping each other. Its usefulness is quite limited however since it cannot be determined which two sprites collided and where.

6

Set when there are too many sprites on a scan line i.e. over 17 in 32-cell mode or over 21 in 40-cell mode.

7

Set when a vertical interrupt occurs, presumably cleared at the end of a frame.

8

FIFO full flag. The VDP has a FIFO which can hold up to 4 words while the VDP's busy and unable to immediately process the write command. This flag is set when the 4th word is written and the FIFO cannot hold any more data. If the 68000 attempts to write again it will be frozen until at least one of the stored words has been delivered to its destination.

9

FIFO empty flag. This flag is only set when the FIFO is completely empty. If the FIFO is only partially filled both this flag and the FIFO full flag are 0.

10

Unused (always set).

11

Unused (always cleared).

12

Unused (always set).

13

Unused (always set).

14

Unused (always cleared).

15

Unused (always cleared).

Writing to registers

The VDP has 24 write-only registers, each taking bytes, the purpose of which is explained here. A register can be set by writing a word to the control port, which has the following format:

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

1

0

0

RS4

RS3

RS2

RS1

RS0

D7

D6

D5

D4

D3

D2

D1

D0

Bits 15 and 14 are always 1 and 0 respectively to differentiate register writes from access control writes. RS4-RS0 is the register number, from $00 to $17 (writing to registers $18-$1F has no effect), and D7-D0 is the data to write. Since the VDP treats longword access as equivalent to two word accesses, two registers can be programmed at the same time by writing a longword:

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

Setting VDP access

The 68000 can set VDP access by writing a longword to the control port, which has the following format:

31

30

29

28

27

26

25

24

23

22

21

20

19

18

17

16

CD1

CD0

A13

A12

A11

A10

A9

A8

A7

A6

A5

A4

A3

A2

A1

A0

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

0

0

0

0

0

0

0

0

CD5

CD4

CD3

CD2

0

0

A15

A14

A15-A0 is the destination RAM address. CD5 is the DMA flag, and CD4 is set for a VRAM copy DMA operation. Bits CD3-CD0 govern the type of access:

Access type

CD3

CD2

CD1

CD0

VRAM read

0

0

0

0

VRAM write

0

0

0

1

CRAM read

1

0

0

0

CRAM write

0

0

1

1

VSRAM read

0

1

0

0

VSRAM write

0

1

0

1

Data port

After VDP access has been set, data can be read or written through the data port, which is accessed at address $C00000 from the 68000. After each read or write, the address is increased by the value of the auto increment register (register $0F). Attempting to read data after setting up a write operation or vice-versa will cause the read or write to 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 is a read-only port which can be accessed at address $C00008 from the 68000. It keeps track of the position of the TV beam and its value is only meaningful in active scan (i.e. not during vertical blank).

Reading the port will lead to a 2 byte value: the low byte is the horizontal position of the beam and the high byte is the vertical position. Under H40 or interlaced modes, values may be bigger than 255 and hence don't fit. Because of this, the LSB of the vertical counter is replaced by the MSB in interlaced mode, and the LSB of the horizontal counter is always dropped (so it'll have half the real value).