jibby/utils/vram

VRAM manipulation functions.

Example: cmd: --compileOnly -r:off

import jibby/utils/vram
import jibby/utils/codegen

# {.compile: "gfx.asm".}
# Assuming these graphics live in gfx.asm, whereby
# they are defined like:
# _titleScreenGfx: .incbin "title.2bpp"
# _fontGfx: .incbin "font.2bpp"
var
  titleScreenGfx {.importc, asmdefined, noinit.}: uint8
  fontGfx {.importc, asmdefined, noinit.}: uint8

turnOffScreen()
clearVram()
cast[pointer](Tiles1).copyMem(titleScreenGfx.addr, 0x30.tiles)
cast[pointer](Tiles2.offset(' '.ord)).copyMem(fontGfx.addr, 0x60.tiles)
turnOnScreen()

Types

LcdcFlag = enum
  BgEnable = 0, ObjEnable, ObjTall, UseBgMap1, UseTiles0, WinEnable, UseWinMap1,
  LcdOn
StatFlag = enum
  StatVblank = 0,           ## Set during Mode 1
  Busy,                     ## Set during Modes 2 and 3
  Coincidence,              ## LY == LYC
  ModeHblankSelect,         ## LCD interrupt fires at Hblank
  ModeVblankSelect,         ## LCD interrupt fires at Vblank
  ModeOamScanSelect,        ## LCD interrupt fires at OAM Scan
  LycSelect                  ## LCD interrupt fires at LY == LYC
StatModes = enum
  ModeHblank = 0,           ## Mode 0
  ModeVblank,               ## Mode 1
  ModeOamScan,              ## Mode 2
  ModeActive                 ## Mode 3
VramTilemap = distinct array[32 * 32, byte]
VramTileset = distinct array[128 * 16, byte]

Consts

BgMap0: ptr VramTilemap = 0x9800'u16
BgMap1: ptr VramTilemap = 0x9C00'u16
BgMapWidth = 32
BgPal: ptr byte = 0xFF47'u16
rBGP
InvertedPalette = 0b00011011'u8

An inverted dark -> light palette, use it with the ...Pal constants like:

BgPal[] = InvertedPalette

LcdControl: ptr LcdcFlags = 0xFF40'u16
rLCDC
LcdStat: ptr StatFlags = 0xFF41'u16
rSTAT
LineY: ptr byte = 0xFF44'u16
rLY
NormalPalette = 0b11100100'u8

The normal light -> dark palette, use it with the ...Pal constants like:

BgPal[] = NormalPalette

ObjPal0: ptr byte = 0xFF48'u16
rOBP0
ObjPal1: ptr byte = 0xFF49'u16
rOBP1
ScrollX: ptr byte = 0xFF43'u16
rSCY
ScrollY: ptr byte = 0xFF42'u16
rSCX
SpritePalette = 0b10010000'u8

This is the normal palette but shifted by one lighter, since the first color is transparent. Use it with the ...Pal constants like:

ObjPal0[] = SpritePalette

TileBytes = 16
Number of bytes needed to represent one 2bpp tile.
Tiles0: ptr VramTileset = 0x8000'u16
Tiles1: ptr VramTileset = 0x8800'u16
Tiles2: ptr VramTileset = 0x9000'u16
TilesAmount = 128
Number of tiles available in a single tile set.
WinX: ptr byte = 0xFF4B'u16
rWX
WinY: ptr byte = 0xFF4A'u16
rWY

Procs

proc copy1bppFrom(toAddr: VramPointer; fromAddr: pointer; size: Natural)
A special version of VRAM copyMem for copying 2-color (1bpp) tile data. The size parameter should be what the size would have been if copyMem were called instead.

Example: cmd: --compileOnly -r:off

from jibby/utils/codegen import asmDefined
import jibby/utils/vram

var monochromeFont* {.importc, asmDefined, noinit.}: uint8
Tiles0.offset(0).copy1bppFrom(monochromeFont.addr, 0x10.tiles)
proc copyMem(toAddr: VramPointer; fromAddr: pointer; size: Natural)
Copy some data to VRAM even when the screen is still on.
Tip: For displaying text to the screen, you should use the print: print function.

Example: cmd: --compileOnly -r:off

import jibby/utils/vram

let message = "Hello"
BgMap0.offset(0, 0).copyMem(message[0].addr, message.len)
proc setMem(toAddr: VramPointer; value: byte; size: Natural)
Fill VRAM locations even when the screen is still on.

Example: cmd: --compileOnly -r:off

import jibby/utils/vram

# clear the entire screen
BgMap0.setMem(0, sizeof(BgMap0))
proc turnOffScreen(): void {....raises: [], tags: [], forbids: [].}
Safely turns off the LCD. According to the Pan Docs, the screen cannot be turned off unless rLY hits V-blank.

Example: cmd: --compileOnly -r:off

from jibby/utils/codegen import asmDefined
import jibby/utils/vram

var hugeGfx {.importc, asmDefined, noinit.}: array[0x100.tiles, byte]

# Copying large graphics. To make this a simple memory-copy operation,
# the screen needs to be turned off beforehand.
turnOffScreen()
copyMem(
  cast[pointer](Tiles0), # Since the screen is turned off
  hugeGfx.addr,
  0x100.tiles,
)
turnOnScreen()
proc waitFrame(): void {....raises: [], tags: [], forbids: [].}
Waits for the next VBlank interrupt.

Templates

template `[]=`(base: ptr VramTilemap; which: int; val: byte)
Convenience for setting a raw value in the background map. Be wary of using it with offset.

Example: cmd: --compileOnly -r:off

import jibby/utils/vram

BgMap0[0] = 0x7f'u8

# Be wary of going out of bounds here, since the bounds are
# still the same
BgMap0.offset(1, 4)[0] = 0x50'u8
template `[]=`(base: ptr VramTileset; which: int; val: byte)
Convenience for setting a raw value in the tileset. Be wary of using it with offset.

Example: cmd: --compileOnly -r:off

import jibby/utils/vram

Tiles1[0] = 0x7f'u8

# Be wary of going out of bounds here, since the bounds are
# still the same
Tiles1.offset('J'.ord)[0] = 0x50'u8
template clearVram()
Clear the entirety of VRAM0. This should be called only when the screen is disabled.
template copyDoubleFrom(toAddr: VramPointer; fromAddr: pointer; size: Natural)
Alias for copy1bppFrom.
template disableLcdcFeatures(i: LcdcFlags): untyped
Disable rLCDC flags. If you try to disable LcdOn (LcdcFlag) using this, this will error out and you would be advised to use turnOffScreen() instead.

Example: cmd: --compileOnly -r:off

import jibby/utils/vram

disableLcdcFeatures({WinEnable})

when false:
  # compiler error!
  disableLcdcFeatures({LcdOn})
else:
  turnOffScreen()
template enableLcdcFeatures(i: LcdcFlags): untyped
Enable rLCDC flags.

Example: cmd: --compileOnly -r:off

import jibby/utils/vram

enableLcdcFeatures(
  {
    BgEnable, # Display the background
    UseWinMap1, # Set window to 0x9C00
    LcdOn, # Turn on the screen
  }
)
template offset(base: ptr VramTilemap; x: uint; y: uint): ptr VramTilemap
Returns the memory location of some offset into the VRAM tile map address specified in base. All positions are relative to the top left.

Example: cmd: --compileOnly -r:off

import jibby/utils/vram

discard BgMap0.offset(1, 1) # 0x9821
template offset(base: ptr VramTileset; tile: uint): ptr VramTileset
Returns the memory location of some offset into the VRAM tile set address specified in base. The argument specifies how many tiles to offset it with.

Example: cmd: --compileOnly -r:off

import jibby/utils/vram

discard Tiles1.offset(1) # 0x8810, tile #1 of tileset 0x8800
template tiles(i: Natural): int
Length of 2bpp tiles in bytes.

Example: cmd: --compileOnly -r:off

import jibby/utils/vram

discard 0x60.tiles # 0x600
template turnOnScreen(): untyped
Convenience for enabling the LCD.
template waitVram()
Waits for the next rSTAT interrupt.

Example: cmd: --compileOnly -r:off

import jibby/utils/vram

# Writes a single byte to the tileset.
waitVram()
Tiles0[0] = 11'u8