Discussion about software development for the old-school Gameboys, ranging from the "Gray brick" to Gameboy Color
(Launched in 2008)
You are not logged in.
Greetings,
Apologies if this question has already been answered; a brief search for "vram tilemap" didn't yield the answers I was hoping for, but please link me to a resource if I missed it. My development environment consists of vi, vasm, and rgbfix -- no GBDK, mostly just following Keith's multi-platform Z80 tutorials at ChibiAkumas and referring to SM83 opcode charts at GBdev and PastRaiser.
My question: What is the best non-static method for obtaining a bitmap's initial byte address in VRAM? I have a function that copies 2bpp bitmaps into VRAM, so creating a label has proved fruitless both before the copy and inside of it. Those labels have just given me the address of my "incbin" and "call" opcodes respectively, and my instincts say that "org" in VRAM is a bad idea for maintainability.
The VRAM consists of a 96-character 1bpp font copied in per Keith's tutorial (&8000 - &85FF), then a couple of 2bpp spritemaps at &8600. Without specifically referring to the Listing file, bgb debugger, or manually tracking bytes, is there a way to know and/or refer to each bitmap copied into VRAM?
I am very new to GB development and assembly in general, but have recently come to understand how bitplanes fit into &8000 - &8FFF VRAM and how the TileMap &9800 - &9BFF contains pointers to VRAM offsets. I found the 16-bit bitshift routine below on ChilliAnt's website and use it to convert a VRAM address into its TileNumber offset:
ConvertVRAMtoTile: ; Shifts HL 4 positions right (divides by 16?)
srl h ; This bitshift courtesy of chilliant.com/z80shift.html
rr l
srl h
rr l
srl h
rr l
srl h
rra
rr l
ld a, l
retEDIT 1: found bugs with my original VRAM conversion, and didn't want future ASM seekers to get hung up on it.
EDIT 2: The 4-shift method broke after &87F0, so I came up with this hackiness:
ConvertVRAMtoTile:
; &87 1000 0111 &88 1000 1000
; &F0 1111 0000 &00 0000 0000
; need 0111 1111 need 1000 0000
;
; 1. zero top nibble of H
; 2. zero bottom nibble of L
; 3. XOR H and L together
; 4. swap L (it's in A)
;
ld a, h ; zero top nibble of H
and a, %00001111
ld h, a
ld a, l ; zero bottom nibble of L
and a, %11110000
ld l, a
xor a, h ; A contains L, so XOR it with H
swap a ; don't forget to SWAP!
retIn case anyone's counting, this AND/XOR/SWAP method takes up 10 bytes in ROM and 13 cycles (52 T-states?) whereas the 4xBITSHIFT function from ChilliAnt uses 19 bytes of ROM and takes 22 cycles -- according to PastRaiser's SM83 opcode chart, of course.
~JV
Last edited by judgevorak (2026-02-03 00:41:23)
Offline
I settled on managing memory locations for each bitmap copied into VRAM; this may not scale well when VRAM is full and needs shuffling, but I'll burn that bridge when I get to it. In practice, I've copied all my bitmaps consecutively so the font is &8000 - &85FF, then Alien at &8600 - &863F, and Flower at &8640 - &86FF
Here's my hacky method of obtaining the initial byte of a bitmap copied into VRAM:
1. Declare two bytes of memory per bitmap
bmpAlienH equ &c000
bmpAlienL equ &c0012. Store HL values just before CopyBitmapLoop
ld de, AlienSprite ; incbin label
ld a, h ; prepare to store bmpAlienH
ld (bmpAlienH), a
ld a, l ; prepare to store bmpAlienL
ld (bmpAlienL), a
ld bc, AlienSpriteEnd-AlienSprite ; counter for CopyBitmapLoop
call CopyBitmapLoop3. Refer to those memory locations for DrawSprite functions
ld a, (bmpAlienH) ; ye olde 8-bit shufflee
ld h, a
ld a, (bmpAlienL)
ld l, a ; now HL is AlienSprite VRAM addr
call CalcTileOffset ; bangs &8600 into &60
call DrawFunction ; render bitmap at VRAM addr HLOffline