-
Notifications
You must be signed in to change notification settings - Fork 60
(VERA 0.9) 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.
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.
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:
10 POKE$9F20,0
20 POKE$9F21,0
30 POKE$9F22,0
40 POKE$9F23,1
This is identical to doing VPOKE0,0,1
.
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:
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)
.
stz $9F20
stz $9F21
stz $9F22
lda $9F23
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:
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
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:
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
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
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.
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
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
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.