Chroni Series

Chroni Part 2: VRAM access

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