Skip to content

(VERA 0.9) Getting Started

Stephen Horn edited this page Mar 16, 2020 · 2 revisions

Getting Started

Many of the VERA's settings are memory-mapped into the address space of the Commander X16. This means that we access them by reading and writing to locations in memory, as if the VERA were sharing that memory with us. The addresses used by the VERA run from $9F20 through $9F3F.

This page will focus on $9F20-$9F23, the basics of communicating with the VERA:

Address Name Description
$9F20 VERA_ADDR_LO Contains the least-significant byte of VRAM we want to access
$9F21 VERA_ADDR_MID Contains the middle byte of VRAM we want to access
$9F22 VERA_ADDR_HI Contains the most-significant bit of VRAM we want to access and sets the memory access increment for VERA data ports
$9F23 VERA_DATA0 Data port 0

For experienced 8-bit and 16-bit programmers accustomed to thinking about 24-bit addresses, it's tempting to think of VERA_ADDR_LO, VERA_ADDR_MID, and VERA_ADDR_HI as the low, "high", and "bank" bytes of an address, and many assemblers would follow this nomenclature as well. Technically, the VERA has no concept of "banks", it just has a 20-bit wide VRAM address space.

VRAM

The VERA has 129,472‬ bytes of VRAM (exactly 1,600 bytes less than 128KiB) starting at address $00000, running through $1F9BF. The addresses $1F9C0-$1FFFF are reserved for other features of the VERA. VRAM is accessed through registers $9F20-$9F23.

Writing and reading with the VERA

At system startup, the kernal maps a layer to start at VRAM address $00000, and leaves the VERA configured to use VERA_DATA0 for reading and writing. $00000 corresponds to the top-left corner of the screen, so we can write a character to it through VERA_DATA0 with the following examples:

BASIC

10 POKE$9F20,0
20 POKE$9F21,0
30 POKE$9F22,0
40 POKE$9F23,1

This is identical to doing VPOKE0,0,1.

6502

stz $9F20
stz $9F21
stz $9F22
lda #1
sta $9F23

After setting the low, mid, and high bytes of our desired VRAM address to $00, this places a purple "A" in the top-left corner of the screen by writing $01 to VERA_DATA0.

We can read from the VERA exactly the same way as we wrote to it, but reading from VERA_DATA0 instead of writing to it:

BASIC

10 POKE$9F20,0
20 POKE$9F21,0
30 POKE$9F22,0
40 PRINT PEEK($9F23)

This is identical to doing PRINT VPEEK(0,0).

6502

stz $9F20
stz $9F21
stz $9F22
lda $9F23

Reading or writing a sequence: The memory access increment

The upper 4 bits of register $9F22 contains the "memory access increment" value, which causes VERA_DATA0 to automatically increment its address after each read or write.

Increment value Increment amount
0 0
1 1
2 2
3 4
4 8
5 16
6 32
7 64
8 128
9 256
10 512
11 1024
12 2048
13 4096
14 8192
15 16384

This can be useful when we want to quickly read or write a series of bytes to the VERA without needing to setup the VRAM address in-between each byte. For instance, if we wanted to write "HELLO WORLD" to the console after startup, we might do the following:

BASIC

10 REM "HELLO WORLD"
20 DATA 8,5,12,12,15,$60,23,15,18,12,4
30 POKE$9F20,0 : POKE$9F21,8 : POKE$9F22,$20
40 REM PRINT HELLO WORLD TO SCREEN
50 FOR X=1 TO 11 : READ A : POKE$9F23,A : NEXT

6502

hello: 
    !byte $08, $05, $0C, $0C, $0F, $60, $17, $0F, $12, $0C, $04 ; "HELLO WORLD"

print_hello:
    stz $9F20
    stz $9F21
    lda #$20
    sta $9F22
    ldy #0
loop:
    lda hello,y
    sta $9F23
    iny
    cpy #11
    bne loop
    rts

This chooses a memory access increment value of "2", because the VERA's default settings use a character size that is 2 bytes wide: The first byte is the character, the second is coloring information. By skipping every other byte this way, we can write text without changing the color information.

If we'd wanted to change the color information as well, we could have done so easily with a different increment value:

BASIC

10 REM "HELLO WORLD"
20 DATA 8,5,12,12,15,$60,23,15,18,12,4
30 POKE$9F20,0 : POKE$9F21,8 : POKE$9F22,$10
40 REM PRINT HELLO WORLD TO SCREEN
50 FOR X=1 TO 11 : READ A : POKE$9F23,A : POKE$9F23,33 : NEXT

6502

hello: 
    !byte $08, $05, $0C, $0C, $0F, $60, $17, $0F, $12, $0C, $04 ; "HELLO WORLD"

print_hello:
    stz $9F20
    stz $9F21
    lda #$10 ; Increment of 1 instead
    sta $9F22
    ldy #0
loop:
    lda hello,y
    sta $9F23
    lda #33
    sta $9F23
    iny
    cpy #11
    bne loop
    rts

Incrementing backwards (a.k.a. decrementing)

There may be some cases where you want to automatically decrement the VRAM address that VERA_DATA0 accesses, instead of incrementing it. This can be done by setting the flag $08 on register $9F22. This flag reverses the direction of VERA_DATA0's automatic address changes, from increments to decrements.

BASIC

10 REM "HELLO WORLD"
20 DATA 8,5,12,12,15,$60,23,15,18,12,4
30 POKE$9F20,11 : POKE$9F21,8 : POKE$9F22,$18
40 REM PRINT HELLO WORLD TO SCREEN
50 FOR X=1 TO 11 : READ A : POKE$9F23,A : POKE$9F23,33 : NEXT

6502

hello: 
    !byte $08, $05, $0C, $0C, $0F, $60, $17, $0F, $12, $0C, $04 ; "HELLO WORLD"

print_hello:
    lda #11
    sta $9F20
    stz $9F21
    lda #$18 ; Increment of -1 instead
    sta $9F22
    ldy #0
loop:
    lda hello,y
    sta $9F23
    lda #33
    sta $9F23
    iny
    cpy #11
    bne loop
    rts

Why use POKE instead of VPOKE?

In truth, when writing BASIC programs, there is relatively little reason to use POKE over VPOKE when communicating with the VERA. The overhead from using BASIC in either case vastly exceeds almost any performance gains you could expect from POKE.

Assembly programmers don't have VPOKE, however, and so must rely on the memory-mapped registers to interact with the VERA. But when you consider any VPOKE macro they might write will expand to 3 extra pairs of lda/sta instructions for each byte written, setting aside any additional math that may be required to determine the VRAM address, it quickly becomes obvious just how much faster it is to setup VERA_DATA0's automatic address increment once and then quickly stream a block of data to it.