Direct VERA Access

*** Mostly obsolete, valid for Emulator versions before R.37 - Check the VERA Overview instead ***


In this short post we will discuss access to graphics using direct access. We will explain the memory organization and test it with Basic and later translate it into Assembly program

When programing in Basic we have few handy commands that make access to graphics simple and seamless. For example using VPOKE and VPEEK we can write and read to and from any address in video memory. With VLOAD we can load chunks of data directly into Video memory. So why would accessing it with machine code be any different?
To explain let’s look at the (simplified) memory map of Commander X16.



The CPU is using 16 bit address bus and therefore can directly access 64 Kbytes of memory. We call it CPU memory. Commander X16 actually has more memory which CPU can access through “banking switching” however it can still only address 64 at any given time.
On the other hand we 128K of Video memory (VRAM). That memory is completely separated and isolated from CPU memory therefore CPU has no direct access to it. The only way to manipulate the data in Video memory and therefore the content of the display is through a fairly narrow window in the CPU memory.
Currently 16 bytes are reserved for VERA communication but only 8 bytes are documented for our use and are located on memory locations $9F20 - $9F27. That is fairly narrow pipe that we have to feed through all the data for the graphics.
Let’s look at those 8 bytes or registers that we can use:

Register Address Name Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
0 $9F20 Address LO Address (Bits 0 - 7)
1 $9F21 Address MI Address (Bits 8 - 15)
2 $9F22 Address HI Increment Address (Bits 16 - 19)
3 $9F23 Data 0 Data Register 0
4 $9F24 Data 1 Data Register 1
5 $9F25 VERA Control Reset Select
6 $9F26 Interrupt Enable UART SPRCOL LINE VSYNC
7 $9F27 Interrup Status UART SPRCOL LINE VSYNC

First three registers are used to set address into VRAM. We have 20 bits at our disposal (0 – 19). That gives us a range from $00000 to $FFFFF or 1M bytes. Of course it is no coincidence that VPOKE command has the same range. Since we said that we only have 128K of VRAM clearly we can’t use all the addresses but let’s remember important addresses from Sprites in Basic I article:

Address Range Description
$00000 - $1FFFF Video RAM
$F0000 - $F001F Display composer registers
$F1000 - $F11FF Palette
$F2000 - $F200F Layer 0 registers
$F3000 - $F300F Layer 1 registers
$F4000 - $F400F Sprite registers
$F5000 - $F53FF Sprite attributes
$F6000 - $F6xxx Audio
$F7000 - $F7001 SPI
$F8000 - $F8003 UART

We see that video memory is between $00000 and $1FFFF and that we have Display Composer, Palette, sprite registers etc. from $F0000 on. That means that if we want to manipulate VRAM we can use following binary values of 00 or 01 in the High byte of address and %1111 or $F in hex for accessing internal VERA registers.
After setting the address we can use Data registers to write or read from Video Memory. We can use Register 3 ($9F23) as Data Register 0 and Register 4 ($9F24) as Data Register 1. We can choose which one we prefer by setting Bit 0 in Register 5 ($9F25) with value 0 selecting Data Register 0 and value 1 selecting Register 1. Note that Bit 7 in this register resets the VERA settings but I recommend not to use it from BASIC.
Next we have to explain probably the most important concept of using VERA and accessing Video Memory. Register 2 ($9F22) contains four highest bits of address but also Increment.

Increment is setting that defines automatic increment of VERA address in registers 0-2 after every read or write. Since we have 4 bits we have 16 different values but what does that actually mean.
The best way to describe how increment works let’s imagine following example and test it. As you remember default video mode of Commander X16 after start (or reset) is 80 column text mode. The displayed text starts in Video memory $00000 which we can verify by VPOKE to that address. The memory is organized in a way that first byte contains the character code. The second character contains the color attributes, the third second character displayed, fourth color attribute for that character and so on. So if we want to write only characters we have to write to every other address starting with $00000, then $00002 followed by $00004 and so on. Obviously the address has to be incremented by two for each write to the screen. And that is exactly what the Increment setting does automatically.
So let’s test this theory and write a simple BASIC program to write to the video memory without using VPOKE but just POKE to the VERA external registers and see if we can recreate above scenario.
First step is to decide which data register we will use. Let’s just use Register 0. We do that with
POKE $9F25,0
Next we have to set screen address to $00000 and set Increment to 2 so we can write to every other address. That means we set Low and Mid byte to 0 and Hi Byte to $20 Hex.
With POKE $9F20,0:POKE$9F21:POKE $9F22,$20
If we write some value to Data Register 0 ($9F23) it should display the character in the top left corner and increment the address by two. To make it more interesting let’s put all this into a program:


The Increment values are not simply represented by binary value from 0 – 15 but they have the following values to increase the reach of the Increment:

Increment Setting Actual Increment Amount
0 or $0 0
1 or $1 1
2 or $2 2
3 or $3 4
4 or $4 8
5 or $5 16
6 or $6 32
7 or $7 64
8 or $8 128
9 or $9 256
10 or $A 512
11 or $B 1024
12 or $C 2048
13 or $D 4096
14 or $E 8192
15 or $F 16384

Knowing this we can modify the above program and try to write vertically. If we recall in the default mode each line takes 256 bytes of memory and only part is visible. 80 out of 128 characters and attributes. To write vertically we therefore have to increment memory location pointer by 256 after every write. Based on a table above we have to use value 9 for increment and now our code looks like this:


No real surprises there. We could use what we learned in Basic and in special cases we might be able to speed up the code a little but not in significant way, especially not in real life scenarios.

Assembly

The logical next step is to use the above learned in assembly. Since POKE command is closest we get to hardware from Basic the translation is very simple. Let’s look at below Assembly code and walk through it. Commander X16 comes with Monitor built in which is very convenient. It allows us write simple assembly programs right there on the system without complicated setup and external tools or installing additional tools on a system itself.


First three lines of code simply write 0 into registers at $9F25, $9F20 and $9F21. Mnemonic STZ (STore Zero) was added to 65C02 so if you try to use it on Commodore 64 or any other 6502 computer it will not be recognized.
Lines 4 and 5 first stores value $20 hex into Accumulator, which is the main register in the CPU and store it into $9F22 essentially setting the Memory address to $00000 and setting increment to 2.
We will use Accumulator for two things, to store the value of the next character to be written to screen and as a counter to write exactly 80 characters. Since we will start with A, which has screen code 1 (just like in the Basic program) we initialize it by setting it to 1 in Line 6.
Lines 7-10 are our loop. We write 1 to first location in video memory which in first iteration is $00000. Then in line 8 we increment Accumulator by one. In line 9 we compare it with value $51 hex, which is 81 in decimal. If we would compare with 80 the loop would exit too soon (we could use BPL but let’s keep it simple for now). Then in line we check the result of comparison and if the comparison was not equal we jump back to the beginning of the loop to line 7 (address $4010).
During second iteration therefore we write 2 into address $00002, then 3 into $00004 and so on until we write all 80 characters.
In line 11 we ReTurn from Subroutine and go back to basic or wherever we called this function from.
Like every piece of code this routine could be written in many different ways but I feel this is the clearest way to do it.
Before we wrap up let’s quickly look at how to write it and test it. At the end of this part our screen should look similar to this:

We have to start Monitor by writing command MON
Our cursor will be waiting after the prompt in the form of dot.
Without any additional preparation we can start writing code by telling Monitor we will write assembly and at what address by using command A immediately followed by mnemonic of the first assembly command and parameter (if any). In our case we just type:

A4000 STZ $9F25

After that monitor will expand the line and show the actual bytes stored in memory at locations $4000 - $4002 like we see on above screenshot and wait for next command in next line with new address $4003. So we just keep typing the whole program and when finished on memory location $4019 we simply press enter and we will be at dot prompt again. By typing X we can exit monitor.
All we have to do now is call the program by calling it from Basic by:

SYS $4000

And we should see 80 characters magically appear in the first line of the screen.

Have fun experimenting with and changing your first assembly program.

Comments

Popular posts from this blog

Default Palette

Hello VERA (BASIC vs C vs Assembly)