Setting up the VDP

Before doing anything with the video hardware we need to get it ready, so let's get going on that. We'll also get ready some things we're going to need in the graphics articles elsewhere in the site.

What we need

We're going to reference this stuff all over the site.

First of all we need to understand how to talk to the video hardware. The VDP has three ports (each with their own address), that can be accessed as 16-bit or as 32-bit (the latter is the same as two 16-bit accesses):

Now that we know what hardware ports exist let's make labels for their addresses:

VdpCtrl:    equ $C00004  ; VDP control port
VdpData:    equ $C00000  ; VDP data port
HvCounter:  equ $C00008  ; H/V counter

The other thing we need to know about is the VDP registers. These registers change how the video hardware behaves. For a full explanation of what they do there's the VDP register reference, but don't worry since the registers are going to be explained properly anyway in the relevant pages in this site.

For now just make sure to keep this list at hand (we're going to use it all the time):

VDPREG_MODE1:     equ $8000  ; Mode register #1
VDPREG_MODE2:     equ $8100  ; Mode register #2
VDPREG_MODE3:     equ $8B00  ; Mode register #3
VDPREG_MODE4:     equ $8C00  ; Mode register #4

VDPREG_PLANEA:    equ $8200  ; Plane A table address
VDPREG_PLANEB:    equ $8400  ; Plane B table address
VDPREG_SPRITE:    equ $8500  ; Sprite table address
VDPREG_WINDOW:    equ $8300  ; Window table address
VDPREG_HSCROLL:   equ $8D00  ; HScroll table address

VDPREG_SIZE:      equ $9000  ; Plane A and B size
VDPREG_WINX:      equ $9100  ; Window X split position
VDPREG_WINY:      equ $9200  ; Window Y split position
VDPREG_INCR:      equ $8F00  ; Autoincrement
VDPREG_BGCOL:     equ $8700  ; Background color
VDPREG_HRATE:     equ $8A00  ; HBlank interrupt rate

VDPREG_DMALEN_L:  equ $9300  ; DMA length (low)
VDPREG_DMALEN_H:  equ $9400  ; DMA length (high)
VDPREG_DMASRC_L:  equ $9500  ; DMA source (low)
VDPREG_DMASRC_M:  equ $9600  ; DMA source (mid)
VDPREG_DMASRC_H:  equ $9700  ; DMA source (high)

Setting up the VDP

The absolutely first thing we need to do is read back the control port. Why? Because the console may have been reset at just about any point and you don't know what was going on with the VDP. Reading back from the control port cancels whatever was going on and puts it back into a well known state.

No need to do anything with the value we read, so just testing will do:

    tst.w   (VdpCtrl)

Then we have to set the registers. This is done by taking the constants above, then putting the new value into the low byte, then writing the resulting word to the control port. For now just leave them as shown below, then as you learn the relevant parts you can change them (we don't write the DMA registers since they aren't relevant yet):

    lea     (VdpCtrl), a0
    
    move.w  #VDPREG_MODE1|$04, (a0)    ; Mode register #1
    move.w  #VDPREG_MODE2|$04, (a0)    ; Mode register #2
    move.w  #VDPREG_MODE3|$00, (a0)    ; Mode register #3
    move.w  #VDPREG_MODE4|$81, (a0)    ; Mode register #4
    
    move.w  #VDPREG_PLANEA|$30, (a0)   ; Plane A address
    move.w  #VDPREG_PLANEB|$07, (a0)   ; Plane B address
    move.w  #VDPREG_SPRITE|$78, (a0)   ; Sprite address
    move.w  #VDPREG_WINDOW|$34, (a0)   ; Window address
    move.w  #VDPREG_HSCROLL|$3D, (a0)  ; HScroll address
    
    move.w  #VDPREG_SIZE|$01, (a0)     ; Tilemap size
    move.w  #VDPREG_WINX|$00, (a0)     ; Window X split
    move.w  #VDPREG_WINY|$00, (a0)     ; Window Y split
    move.w  #VDPREG_INCR|$02, (a0)     ; Autoincrement
    move.w  #VDPREG_BGCOL|$00, (a0)    ; Background color
    move.w  #VDPREG_HRATE|$FF, (a0)    ; HBlank IRQ rate

Huzzah, now the VDP is up and ready to use!

Clearing memory

But we're not done yet. Unlike what an emulator may tell you, when the console turns on, video memory will be full of garbage. When you press the Reset button this is also the case. So it may be a good idea to clean it first.

There are three video memories (and we need to clean them all):

To write to those memories, first we write a 32-bit command to the control port (this tells the VDP what we want to do). Then we write what we want to the data port. In this case, it'll be all zeroes. We have to always write to the data port, not consecutive addresses (however, both 16-bit and 32-bit writes will work).

OK, let's get started. The commands for each memory are these:

VRAM_ADDR_CMD:  equ $40000000
CRAM_ADDR_CMD:  equ $C0000000
VSRAM_ADDR_CMD: equ $40000010

We also need to know how large each memory is (sizes in bytes):

VRAM_SIZE:    equ 65536
CRAM_SIZE:    equ 128
VSRAM_SIZE:   equ 80

Now let's get going, we basically have three loops (one for each memory) where we write lots of zeroes to them. Note the division by 4 (since each iteration writes 4 bytes) and the -1 (because the dbf instruction stops iterations at -1, not 0).

    ; Stuff we'll use a lot
    moveq   #0, d0          ; To write zeroes
    lea     (VdpCtrl), a0   ; VDP control port
    lea     (VdpData), a1   ; VDP data port
    
    ; Clear VRAM
    move.l  #VRAM_ADDR_CMD, (a0)
    move.w  #(VRAM_SIZE/4)-1, d1
@ClearVram:
    move.l  d0, (a1)
    dbf     d1, @ClearVram
    
    ; Clear CRAM
    move.l  #CRAM_ADDR_CMD, (a0)
    move.w  #(CRAM_SIZE/4)-1, d1
@ClearCram:
    move.l  d0, (a1)
    dbf     d1, @ClearCram
    
    ; Clear VSRAM
    move.l  #VSRAM_ADDR_CMD, (a0)
    move.w  #(VSRAM_SIZE/4)-1, d1
@ClearVsram:
    move.l  d0, (a1)
    dbf     d1, @ClearVsram

Feel free to optimize the loops when you're more confident.

Do not use the clr instruction for this. Because of a quirk of the 68000, that instruction will first do a dummy read before writing back a 0, and the VDP gets confused if this happens. Don't use clr when dealing with any hardware registers.