Gameboy Development Forum

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.

#1 2021-10-04 14:46:48

ExploreZip
New member
Registered: 2021-10-04
Posts: 3

ROM Feedback

Hi! I was wondering whether it would be possible to receive some feedback on a ROM I've been working on?

In particular, I'm interested in how the structure of my code can be improved, as well as any other things in general.

Thank you smile


tileMap.asm (main file):

INCLUDE "gbhw.inc"
INCLUDE "memory.asm"
INCLUDE "tileSetExp.z80"
INCLUDE "tileMapTest.z80"
INCLUDE "TitleScreen.z80"
INCLUDE "Sprite.z80"
INCLUDE "npc.z80"

SECTION "Copy_DMA", ROM0[$28]

COPY_DATA:
    pop hl
    push bc

    ld a, [hli] ; will trash OAM
     
    ld b, a

    ld a, [hli] ; will trash sprite OAM
   
    ld c, a

.copy_loop:
    ld a, [hli]
    ld [de], a
    inc de
    dec bc

    ; check counter is zero
    ld a, b
    or a, c
    jr nz, .copy_loop

    ; finish
    pop bc
    jp hl
    reti

SECTION "ROM_entry", ROM0[$100]
    di
    jp Start

REPT $150 - $104
    db 0
ENDR

SECTION "Game Code", ROM0
; VARIABLES
framesLeft EQU 6
spriteArrayLength EQU 4
; VARIABLES

; ARRAYS
spriteArray:
DB $1E, $14, $00, $00 ; YPos, XPos, tile #, attrib
; ARRAYS

; COLLISION TABLE
collisionMap:
DB $00, $01, $02, $01, $01, $01, $01, $01, $01, $01, $01
;COLLISION TABLE

Start:
    nop
    di
    ld sp, $ffff
   
    call OAM_Clear
   
    ld b, spriteArrayLength   
    ld de, spriteArray
    ld hl, sprite
   
    call loadSpriteData

    call DMA_Copy

    call waitVBlank

    call $ff80
   
    call waitVBlank ; wait for VBlank before turning the screen off
   
    ld hl, $ff40
    res 7, [hl]
    ld hl, 0

    ld hl, $8800 ; destination
    ld de, BTile
    ld bc, BTileEnd - BTile
    call memCopyVRAM
   
    call splash_screen
   
    call waitVBlank
   
    ld hl, $ff40
    res 7, [hl]
    ld hl, 0
   
    call drawMap

    ld hl, $8000
    ld de, Sprite
    ld bc, SpriteEnd - Sprite
    call memCopyVRAM
   
    call counterSet
       
    ld a, %10001011 ; turn screen on
    ld [$ff40], a
   
    ld b, framesLeft ; frame number we want to copy to OAM
    ld hl, frames
   
    call updateSprite
   
updateSprite:

.FrameArray:
    ; pop af
    ; loop through array starting at [0]
    ld a, [hl] ; load the contents of frames into a
    ld [sprite+2], a ; load into OAM location
   
    call waitVBlank ; wait for VBlank
    call $ff80 ; call DMA routine from HRAM
   
    inc hl ; increment the frame number
    dec b ; decrement the number of frames left
   
    jr nz, .FrameArray
   
    ;call movement ; read the joypad after looping through the animation
    push hl 
    call waitVBlank
    call movement ; read joypad and move
    pop hl
   
    ; if the currentFrame is zero, then load 6 and loop to FrameArray
    ld b, framesLeft
    ld hl, frames    
    jr .FrameArray
   
loadSpriteData:

.SpriteData
    ; loop through array
    ld a, [de] ; take one byte from array
    ld [hli], a ; increment byte in reserved memory
   
    ; YPos, XPos, tile number, attribute
   
    inc de
    dec b    
   
    jr nz, .SpriteData
   
    ret
   
splash_screen:
    call clear_bg
   ; perform manually for now
    ld hl, $9c00
    ld de, titleScreen
    ld bc, titleScreenEnd - titleScreen
    call memCopyVRAM
 
    ld a, %10001001 ; turn screen on
    ld [$ff40], a

.waitButtonPress
   ld a, %00010000        
   ld [$ff00], a           
   
   ld a, [$ff00]           
   ld a, [$ff00]           
   and $01       
   cp 1
   jr z, .waitButtonPress
   
   ret
   
clear_bg:
   ; clear background screen
   ld hl, $9c00
   ld de, $8800
   ld bc, 32*32
   call memCopyVRAM
   
   ; clear sprite data
   ld hl, $ff40
   res 1, [hl]
   ld hl, 0   
   
   ret

read_joypad:
; read_joypad
   
    ; We need to read the D-Pad to check whether we have any D-Pad input
    ld a, %00100000 ; to get directional keys on D-Pad
    ld [$ff00], a ; load to memory location to perform the read operation
    ld a, [$ff00] ; load results of read operation into accumulator
    ld a, [$ff00] ; do more than once due to bouncing
    cpl
    and %00001111
    ld b, a
   
    ; read buttons
    ld a, %00100000
    ld [$ff00], a
    ld a, [$ff00]
    ld a, [$ff00]
    ld a, [$ff00]
    ld a, [$ff00]
    ld a, [$ff00]
    ld a, [$ff00]
    cpl
    and %00001111
   
    swap a
    or b
    ld b, a
   
    ld a, $30
    ld [$ff00], a
   
    ld a, b
    ;ld [_PadData], a
   
    ret
   
movement:
    ; move sprite depending on the buttons
   
    ; Right    
    ; read joypad
    call read_joypad
    and %00000001
    call nz, moveRight
   
    ; Left
    call read_joypad
    and %00000010
    call nz, moveLeft
   
    ; up
    call read_joypad
    and %00000100
    call nz, moveUp
   
    ; down
    call read_joypad
    and %00001000
    call nz, moveDown
   
    ret
   
moveRight:
    call getSpritePos
   
    inc e 
   
    ld b, d
    ld c, e     
    call collisionDetection
   
    cp a, $01
    ret z   
   
    ld hl, sprite ; load sprite
    inc hl ; [sprite+1] - XPos
        
    inc [hl] ; XPos + 1
   
    call waitVBlank
    call $ff80    
   
    ret

moveLeft:
    call getSpritePos

    dec e
   
    ld b, d
    ld c, e    
    call collisionDetection
   
    cp a, $01
    ret z    
   
    ld hl, sprite
    inc hl
   
    dec [hl] 
   
    call waitVBlank
    call $ff80
   
    ret
   
moveUp:
    call getSpritePos
    ld a, e
    sbc a, $20
    ld e, a
   
    ld b, d
    ld c, e    
    jr c, .carryRegSub
   
    call collisionDetection
   
    cp a, $01 ; look at self modifying code in assembly in book for possible changes - increment instead of decrement
    call nz, moveUpOnce     

    ret
   
.carryRegSub:
   ;ld a, d
   dec b
   ;ld d, a   
   
   ccf

   cp a, $01
   call z, moveUpOnce 
   
moveUpOnce
   ld hl, sprite
   dec [hl]    
   
   call waitVBlank
   call $ff80 
 
   ret
   
moveDown:
   call getSpritePos
   ld a, e
   adc a, $20
   ld e, a
   
   ld b, d
   ld c, e
   jr c, .carryRegAdd
   
   call collisionDetection
   
   cp a, $01
   call nz, moveDownOnce 
   
   ret
   
.carryRegAdd:
   inc b
   
   ccf
   
   call collisionDetection
   
   cp a, $01
   call nz, moveDownOnce   
   
   ret
   
moveDownOnce:
   ld hl, sprite
   ; ld a, [hl]
   
   inc [hl]
   ; ld [sprite], a

   call waitVBlank
   call $ff80
   ret   
   
counterSet:
   ; need to change tile at $8810 to number 0
   ld hl, $9c05 ; destination
   ld [hl], $88 

   ret
   
counterIncrease:
   call waitVBlank
   ld hl, $9c05
   ld a, [hl]
   
   cp a, $91 ; if accumulator contents equal to $91, reset to zero
   jr z, .reset
   
   inc [hl] ; increment to next number tile from byte pointed to by hl
   
   ; increase coin # variable
   ld a, [Coins]
   add a, 1
   ld [Coins], a
   
   ; check if coin is equal to 3
   cp a, $03
   call z, Start   

   ret   
   
.reset
   ld [hl], $88 
   ret     
   
collide_coin:
   call soundData
   call counterIncrease
   call deleteCoin
   ret
   
deleteCoin:
; find the address of the coin to be deleted
; current pos will be in de
 
   ; de is in bc   
   
   ld h, b  ; load the position of the coin
   ld l, c ; hl should now contain the position of the coin
   
   call waitVBlank
   ld a, [hl]
   sub a, $02 
   ld [hl], a
   ret
   
collisionDetection:
   ld a, [de]
   ld de, collisionMap
   sub a, $80
   add a, e
   ld e, a
   ld a, 0
   adc a, d
   ld d, a
   ld a, [de]

   cp a, $02
   call z, collide_coin ; if not equal to $02, collide and return

   ret
   
getSpritePos:
   ld hl, sprite 
   ld a, [hl] ; YPos
   cp a, $10
   jr nc, .moreThanEight
   
   ld a, $10
   
.moreThanEight
   sub a, $10
   srl a 
   srl a
   srl a
   ld de, $0000 ; offset
   ld e, a
   
   ; multiply de by 32
   sla e
   rl d
   sla e
   rl d
   sla e
   rl d
   sla e
   rl d
   sla e
   rl d
   
   ld hl, sprite
   inc hl
   ld a, [hl]
   cp a, 8
   jr nc, .moreThanEight_2
   
.moreThanEight_2
   sub a, 8
   srl a
   srl a
   srl a
   
   add a, e
   ld e, a
   
   ld a, d
   add a, $9c
   ld d, a
   
   ret
   
soundData:
   ; activate sound system
   ld a, %10000000
   ld [$ff16], a
   
   ld a, %11110001
   ld [$ff17], a
   
   ld a, 255
   ld [$ff10], a
   
   ld a, %11000111
   ld [$ff19], a
   
   ret
   
SECTION "OAM vars", WRAM0[$C100]
clear: DS 4*36 ; reserve 144 bytes
sprite: DS 4*1 ; reserve 4 bytes
npc: DS 4*1 ; reserve 4 bytes
Coins: DS 1 ; reserve 1 bytes

SECTION "SpriteAnim", ROMX[$5D00]
; sprite array
frames:
DB $00, $00, $00, $01, $01, $01




memory.asm:

SECTION "Utility", ROM0

memCopyVRAM:
    ld a, [de]
    ld [hli], a
    inc de
    dec bc
    ; check we are at zero
    ld a, c
    or b ; 0 1 = 1 unless 0 0 = 0
    jr nz, memCopyVRAM
    ret

drawMap:
    ld hl, $9c00 ; destination
    ld de, Map ; load map
    ld bc, 32*32 ; MapWidth

.innerloop
    ld a, [de]
    ld [hli], a
    inc de
    dec bc
    ld a, c
    or b
    jr nz, .innerloop
    ret

DMA_Copy:
    ld de, $FF80 ; HRAM dest
    rst $28 ; call VBLANK interrupt vector
    DB $00, $0D ; DMA code is 13 bytes
    DB $F5, $3E, $C1, $the Big N, $46, $FF, $3E, $28, $3D, $20, $FD, $F1, $D9
    ; 13 bytes
    ret

OAM_Clear:
    xor a, a ; clear a register
    ld b, 40*4 ; load 160 bytes into b
    ld hl, clear ; load 160 bytes into hl from shadow OAM

.clear_OAM
    ld [hli], a ; load 0 into a and increment hl
    dec b ; decrement counter until 160 bytes filled
    jp nz, .clear_OAM
    ret

waitVBlank:
    ld a, [$FF44] ; LCDC y-coord
    cp 144
    jr c, waitVBlank
    ret
   
delay:

.loop
    dec bc
    ld a, b
    or c
    jr nz, .loop 
    ret

Offline

 

#2 2021-10-07 15:22:42

tmk
Member
Registered: 2017-05-01
Posts: 63
Website

Re: ROM Feedback

Sure, some quick notes:
- I'd recommend using HW definitions instead of raw registers. It's easier to memorize and to read.
- You don't need nop after Start.
- Stack in HRAM is perfectly fine but I'd use WRAM instead. HRAM is better for crucial variables because you can use one byte shorter and faster instructions (bytes/cycles):

Code:

LD A,(C) - 2/8
LD A,(a16) - 3/16

- I can't see any palette setup?
- You can avoid turning screen on/off by fading all the colors to black or white and do VRAM copy with waiting for access. It's slower but looks proper. smile
- Use interrupts, for vblank especially. There's no reason not to do it. You can place call to SFF80 there.
- Read joypad once per frame and keep results in (H/W)RAM for further processing.
- Try checking macros for 16 bit stuff, multiplying, etc.

I have couple of assembly examples here. You might find something interesting.

Keep up! smile

Last edited by tmk (2021-10-07 15:24:40)

Offline

 

Board footer

Powered by PunBB
© Copyright 2002–2005 Rickard Andersson