The CPU can write or read the 128KB VRAM using a 16KB window at address $A000. There is a special register called VPAGE to select which 16KB is visible in that $A000 – $DFFF range of memory.
This is a simple example to write to the first byte at VRAM
VPAGE = $9009 lda #0 sta VPAGE ; Map VRAM Page 0 at $A000 lda #$FF sta $A000 ; Write $FF at VRAM $0000
This will set VPAGE to write into the next 16KB of VRAM
lda #1 sta VPAGE ; Map VRAM Page 1 at $A000 lda #$CC sta $A000 ; Write $CC at VRAM $4000
VRAM addresses in Chroni
These examples use the VRAM addresses from the CPU point of view, but inside Chroni the whole VRAM is accesible via a 16 bit address. To access the whole 128KB VRAM addresses Chroni multiplies this 16 bit address by two (one bit shift left). So if you need to access VRAM $1254 from Chroni, you need to specify $92A as the address ($1254/2 = $92A).
As you may guess, all Chroni addresses are 16 bit aligned, so you can’t access address $1255 directly. For example if you use the Chroni address $92A, it will be VRAM address $1254, and the next Chroni address $92B will point to VRAM address $1256
VRAM to RAM – RAM to VRAM conversions
You will need to convert addresses from VRAM to RAM and viceversa to setup sprites, palettes, display lists, and more. The current BIOS included with the emulator contains code to do these conversions, you can call those routines or alternatively there is a stdlib.asm file with short routines to call that BIOS code. These routines use predefined system variables to convert from VRAM to RAM and viceversa as follows:
VRAM_TO_RAM -> lib_vram_to_ram -> RAM_TO_VRAM + VRAM_PAGE RAM_TO_VRAM + VRAM_PAGE -> lib_ram_to_vram -> VRAM_TO_RAM
VRAM_TO_RAM is a 16 bit Chroni address
RAM_TO_VRAM is a 16 bit CPU address and VRAM_PAGE is the page value to write on VPAGE
The following example will read the DISPLAY_START Chroni Address and will write the value $41 to it (letter A). The DISPLAY_START is a 16 bit VRAM address and lib_vram_to_ram will convert it to a CPU address.
mwa DISPLAY_START VRAM_TO_RAM jsr lib_vram_to_ram ; result in RAM_TO_VRAM ldy #0 lda #$41 sta (RAM_TO_VRAM), y
Now let’s say that we have a charset written in CPU address $A800 but using VRAM Page 2, we can convert that address to a 16-bit Chroni address using lib_ram_to_vram:
mwa $A800 RAM_TO_VRAM ; Page 1, $A800 is the CPU address mva $#1 VRAM_PAGE jsr lib_ram_to_vram ; result in VRAM_TO_RAM. Chroni address mwa VRAM_TO_RAM VCHARSET ; VCHARSET is a Chroni register
Of course you can use the BIOS functions or your own code to perform these conversions. Following is the code being used to perform these conversions
stdlib.asm short routines to call the BIOS
lib_vram_to_ram: ldx #OS_VRAM_TO_RAM jsr OS_CALL lda VRAM_PAGE sta VPAGE rts lib_ram_to_vram: ldx #OS_RAM_TO_VRAM jmp OS_CALL
graphics.asm These are the BIOS routines that perform the conversion, use them only as a reference to implement your own code if needed.
; vram = (page << 8 << 6 + (addr-VRAM)) / 2 => page << 8 << 5 + (addr-VRAM)/2 ; in : RAM_TO_VRAM with CPU address ; VRAM_PAGE 16K Page in VRAM ; ; out: VRAM_TO_RAM with Chroni address (in words) ram2vram: sbw RAM_TO_VRAM #VRAM lda RAM_TO_VRAM+1 lsr sta VRAM_TO_RAM+1 lda RAM_TO_VRAM ror sta VRAM_TO_RAM lda VRAM_PAGE asl asl asl asl asl ora VRAM_TO_RAM+1 sta VRAM_TO_RAM+1 rts ; page = (vram & 0xE000) >> 5 >> 8 ; addr = (vram & 0x1FFF) * 2 + VRAM ; in: VRAM_TO_RAM with Chroni address (in words) ; out: RAM_TO_VRAM with CPU address ; VRAM_PAGE with 16K Page in VRAM vram2ram: lda VRAM_TO_RAM+1 and #$E0 lsr lsr lsr lsr lsr sta VRAM_PAGE lda VRAM_TO_RAM+1 and #$1F sta VRAM_TO_RAM+1 lda VRAM_TO_RAM asl sta VRAM_TO_RAM rol VRAM_TO_RAM+1 adw VRAM_TO_RAM #VRAM RAM_TO_VRAM rts