Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ; Define hardware macros/shortcuts
- INCLUDE "hardware.inc"
- INCLUDE "hardware_ext.inc"
- ; Interrupt Requests (IRQs)
- ; V-Blank Interrupt Enable (INT 40h) (1=Enable)
- ; LCD STAT Interrupt Enable (INT 48h) (1=Enable)
- ; Timer Interrupt Enable (INT 50h) (1=Enable)
- ; Serial Interrupt Enable (INT 58h) (1=Enable)
- ; Joypad Interrupt Enable (INT 60h) (1=Enable)
- SECTION "IRQ_VBLANK", ROM0[$0040]
- reti
- SECTION "IRQ_LCDC", ROM0[$0048]
- reti
- SECTION "IRQ_TIMER", ROM0[$0050]
- reti
- SECTION "IRQ_SERIAL", ROM0[$0058]
- reti
- SECTION "IRQ_JOYPAD", ROM0[$0060]
- reti
- ; Extra definitions
- PADS_DPAD equ $20
- PADS_BTNS equ $10
- MOVE_TILES equ 8
- MOVE_DELAY equ 30
- TILE_BYTES equ 16
- ; Setup Header of memory type ROM0 (Either ROM0 or ROMX), start at 100h
- ; 100h is where execution of code begins.
- SECTION "Header", ROM0[$100]
- ; Actual code starts at 104, so we have 4 bytes to start our cart.
- EntryPoint:
- di ; Enable Interupts
- jp Begin ; Begin our program!
- ; Pad out remaining header bytes.
- REPT $150 - $104
- db 0
- ENDR
- ; The section of code that'll contain our actual game.
- ; Start location isn't specified, doesn't need to be.
- SECTION "Game", ROM0
- ; Useful functions
- ; Multiplies A by C
- ; @param A - Value
- ; @param C - Multiple
- ; @return Z set, A = result
- Multiply:
- ld d, a
- .multiply_loop
- dec c
- ret z
- add d
- jr Multiply
- ret
- ; Multiplies BC by C
- ; @param BC - Value
- ; @param D - Multiple
- ; @return Z set, BC = result
- Multiply16:
- ld e, a
- .multiply16_loop
- dec d
- ret z
- ld a, c
- add e
- ld c, a
- ld a, b
- adc a
- ld b, a
- jr Multiply16
- ret
- ; Divides A by C
- ; @param A - Divisor
- ; @param C - Dividend
- ; @return C is reset, A = result
- Divide:
- ld d, 0
- .divide_loop:
- sub c
- inc d
- jr nc, .divide_loop
- dec d
- ld a, d
- ret
- ; Delay Frame/Frams
- ; @param A - Amount of frames to delay for
- DelayFrame:
- call WaitForVBlank
- ret
- DelayFrames:
- call DelayFrame
- dec c
- ret z
- jr DelayFrames
- ; Get joypad buttons
- ; @return A - High Nibble: Directional; Low Nibble: Buttons
- getPadButtons:
- ld hl, OLDPAD ; Update old buttons
- ld a, [PADBTNS] ; ^
- ld [hl], a ; ^
- ld a, PADS_DPAD ; Setup joypad to read D-PAD
- ld [rP1], a ; ^
- ld a, [rP1] ; Get results from joypad twice to eliminate noise.
- ld a, [rP1] ; Results will be 1101 1111 If no keys are pressed
- cpl ; Complement A (rP1 almost behaves like active low)
- and $0F ; As the joypad behaves active low we need to filter On/Off bits.
- swap a ; Swap DPAD inputs to MSB, prepare to load buttons in LSB
- ld b, a ; Load DPAD inputs into B
- ld a, PADS_BTNS ; Setup joypad to load buttons
- ld [rP1], a ; ^
- ld a, [rP1] ; Get joypad result
- ld a, [rP1] ; ^
- cpl ; Filter bits, don't swap
- and $0F ; ^
- or b ; DPAD now MSB, BTNS now LSB
- ld [PADBTNS], a ; Save new buttons
- ret
- ; Sets sprite to access by drawSprite
- ; @params A - Sprite index
- ; @returns sprCount = A*4
- setSpriteIndex:
- sla a ; *sprCount = A*4
- sla a ; ^
- ld [sprCount], a ; ^
- sra a
- sra a
- ret
- ; Retrieves Map Tile at Coords
- ; @params HL - Map source ( _SCRN0; _SCRN1 )
- ; @params BC - X,Y
- ; @return A - Tile
- GetMapTile:
- push hl
- ld e, SCRN_VX_B-1 ; (BC * SCRN_X_B-1) + Old B
- ld d, b ;
- ld b, 0 ;
- ld l, c
- .AdjustY
- ld a, c ;
- add l ;
- ld c, a ;
- ld a, b ;
- adc 0 ;
- ld b, a ;
- dec e ;
- jr nz, .AdjustY ;
- ld a, c ;
- add d ;
- ld c, a ;
- ld a, b ;
- adc 0 ;
- ld b, a ;
- pop hl
- add hl, bc ; Load tile ID into A
- ld a, [hl] ;
- ret
- ; Get OAM sprite address bsed on sprCount
- ; @param C = HL offset by n'bytes
- ; @return HL = _OAMRAM + *sprCount
- getSpriteAddr:
- ld a, [sprCount] ; hl = _OAMRAM + *sprCount
- ld hl, _OAMRAM ; ^
- add l ; ^
- add c ; HL += C
- ld l, a
- ret
- ; Sets sprite tile based on *sprCount
- ; @params C - Tile #
- ; @return HL = _OAMRAM + *sprCount + 2
- setSpriteTile:
- ld a, [sprCount] ; hl = _OAMRAM + *sprCount
- ld hl, _OAMRAM ; ^
- add l ; ^
- add 2 ; HL += 2; Move to tile addr
- ld l, a ; ^
- ld [hl], c ; Set sprite tile
- ret
- ; Gets tile coords at sprite in *sprCount
- ; @return BC = Tile coords of sprite.
- getSpriteTileCoords:
- ld c, 0
- call getSpriteAddr ; Entry data for spr
- ld a, [hli] ; C = YPos
- srl a ; Adjust as a tile.
- srl a ; ^
- srl a ; ^
- dec a ; Zero indexed tiles.
- dec a ;
- ld c, a ; ^
- ld a, [hli] ; B = XPos
- srl a ; Adjust as a tile.
- srl a ; ^
- srl a ; ^
- dec a ; Zero indexed tiles.
- ld b, a ; ^
- ret
- ; Flips X sprite in *sprCount
- FlipSpriteX:
- ld c, 3
- call getSpriteAddr
- ld a, [hl]
- xor OAMF_XFLIP
- ld [hl], a
- ret
- ; Flips Y sprite in *sprCount1
- FlipSpriteY:
- ld c, 3
- call getSpriteAddr
- ld a, [hl]
- xor OAMF_YFLIP
- ld [hl], a
- ret
- ; Puts sprite data into _OAMRAM + *sprCount
- ; @params BC - B = XPos, C = YPos
- ; @params DE - D = Sprite Index, E = Sprite attrib
- AddNewSprite:
- ld a, [sprCount] ; hl = _OAMRAM + *sprCount
- ld hl, _OAMRAM ; ^
- add l ; ^
- ld l, a ; ^
- ld a, c
- ld [hli], a ; *YPos = C
- ld a, b
- ld [hli], a ; *XPos = B
- ld a, d
- ld [hli], a ; *Tile = D
- ld a, e
- ld [hli], a ; *Attr = E
- ld a, [sprCount] ; *sprCount += 4
- add $04 ; ^
- ld [sprCount], a ; ^
- ret
- ; Move sprite XY from _OAMRAM + *sprCount
- ; @params BC - B = add XPos, C = add YPos
- ; @return HL at _OAMRAM+*sprCount + 2 (HL at sprite Tile)
- moveSprite:
- call WaitForVBlank
- ld a, [sprCount] ; hl = _OAMRAM + *sprCount
- ld hl, _OAMRAM ; ^
- add l ; ^
- ld l, a ; ^
- ld a, [hl] ; *HL += C
- add c ; ^
- ld [hli], a ; ^
- ld a, [hl] ; *(HL+1) += B
- add b ; ^
- ld [hli], a ; ^
- ret
- ; Turns LCD off for accessing VRAM
- ; @return sets A to 0
- LCD_Off:
- call WaitForVBlank ; Must only be done during VBlank
- xor a ; == ld a, 0
- ld [rLCDC], a ; Clear LCD flags. Results in LCD turning off.
- ret
- ; Turns LCD on for displaying VRAM
- ; @param B - Specify more flags
- ; @return sets A to 128
- LCD_On:
- ld a, %10000000 ; LCDCF_ON
- or b ; Turn on specified flags
- ld [rLCDC], a ; Set display flags
- ; Waits for vertical blank period to begin.
- ; @return rLY >= 144
- WaitForVBlank:
- ld a, [rLY] ; Load LCD draw status
- cp 144 ; Check if LCD is in VBlank time.
- ret nc ; Return if VBlank-ing
- jr WaitForVBlank
- ; Waits for vertical blank period to end.
- ; @return rLY < 144
- WaitVBlankFinished:
- ld a, [rLY] ; Load LCD draw status
- cp 144 ;
- ret c ; Return if VBlank period is over.
- jr WaitVBlankFinished
- ; Copy n'bytes to location
- ; @param DE - Source location
- ; @param HL - Destination
- ; @param BC - Number of bytes to copy
- ; @return B and C equal 0
- Memcpy:
- ld a, [de] ; Grab byte from base
- ld [hli], a ; *HL = a; HL++;
- inc de ; Move to next byte in base
- dec bc ; Decrease bytes to load.
- ld a, b ; 16bit inc/dec don't update flags. Update flags using load.
- or c ; a or c, if b and c are both zero, our copy is done.
- jr nz, Memcpy
- ret ; Return control
- ; Set n'bytes to a value
- ; @param A - Value to set bytes to
- ; @param HL - Destination
- ; @param BC - Number of bytes to set
- ; @returns B and C equal 0
- Memset:
- ld [hli], a ; *HL = a
- dec bc ; Decrease byte set count
- ld e, a ; Backup A
- ld a, b ; Load b into a so we can compare b and c
- or c ; if b or c equals 0; all bytes are set.
- ld a, e ; Restore A
- jr nz, Memset
- ret
- ; Copy string to location
- ; @param DE - Location of String to copy
- ; @param HL - Destination, where to copy string to
- ; @return A equals 0 (Character in string is \0)
- ; @return flags Z set, C reset
- Strcpy:
- ld a, [de] ; a = (char) *de
- or a ; == cp 0
- ret z ; if(a==0) return;
- ld [hli], a ; *hl = a, hl++
- inc de ; de++
- jr Strcpy ; Get next char
- ; Clear/Initialize _OAMRAM
- ClearOAM:
- ; Clear object RAM
- ld hl, _OAMRAM
- ld bc, $A0
- .clearOAM_loop
- xor a ; ld a, 0
- ld [hl], a ; *HL = a
- inc l ; Only do an 8bit inc
- dec bc ; Decrease byte set count
- ld a, b ; Load b into a so we can compare b and c
- or c ; if b or c equals 0; all bytes are set.
- ret z
- jr .clearOAM_loop
- ; Updates background sprite priority depending on if there's a HUD above them.
- UpdateBGSpritesForHUD:
- xor a ; (ld a, 0) Starting with sprite 0
- call setSpriteIndex ; *sprCount = a*4
- .processBGSprite
- ld hl, rLCDC ; Clear all priorties if we're not in the HUD.
- bit LCDCB_WINONOFF, [hl] ; ^
- jp z, .notPriority ; ^
- call getSpriteTileCoords ; BC = Tile XY (BC;XY)
- ld hl, _SCRN0 ; Get map tile from HL
- call GetMapTile ; Get background tile under sprite in _SCRN0
- ld d, a ; Store BG tile in D
- ld hl, _SCRN1 ; Get window tile under sprite in _SCRN1
- add hl, bc ; ^
- ld a, [hl] ; Store WN tile in A
- cp d ; Compare tiles
- jr z, .notPriority ; If tiles are equal, don't prioritize sprite.
- ld c, 3 ; Get sprite attribute byte
- call getSpriteAddr ; ^
- set OAMB_PRI, [hl] ; Set sprite priority
- jr .processNextSprite ; Get next sprite to check.
- .notPriority
- ld c, 3 ; Get sprite attribute byte
- call getSpriteAddr ; ^
- res OAMB_PRI, [hl] ; Clear sprite priority
- .processNextSprite
- ld a, [sprCount] ; Grab current sprite offset
- srl a ; Convert sprite offset to index
- srl a ; ^
- inc a ; Next sprite
- cp 41 ; If we've just processed our last sprite...
- ret z ; Exit.
- call setSpriteIndex ; Set sprite to next.
- jr .processBGSprite ; Proceed to process next sprite.
- ; Macros
- waitjoypad: MACRO
- .wjploop\@: ; The \@ generates a unique label because macro's are "inline"
- call getPadButtons
- ld hl, OLDPAD
- bit \1, [hl]
- jr nz, .wjploop\@
- bit \1, a
- jr z, .wjploop\@
- ENDM
- addHLn16: MACRO
- ld a, l
- add \2
- ld l, a
- ld a, h
- adc \1
- ld h, a
- ENDM
- addDEn16: MACRO
- ld a, e
- add \2
- ld e, a
- ld a, d
- adc \1
- ld d, a
- ENDM
- ; Takes /2 and /3 as X,Y screen coords
- ; Converts X,Y into screen offset, which is put in HL.
- ;
- ; X + Y *= SCRN_VX_B
- ;
- coords: MACRO
- ld e, SCRN_VX_B-1 ; (BC * SCRN_X_B-1) + Old B
- ld d, \1 ; Store X into D
- ld c, \2 ; Store Y into C
- ld l, c ; Backup original value of Y (C)
- ld b, 0 ; B is where all addition that carries over is going to go into.
- .AdjustY\@
- ld a, c ; C *= SCRN_X_B-1
- add l ; ^
- ld c, a ; ^
- ld a, b ; ^
- adc 0 ; Adjust multiplication for 16bits
- ld b, a ; ^
- dec e ; Continue to multiply
- jr nz, .AdjustY\@ ; ^
- ld a, c ; Add X coord
- add d ; ^
- ld c, a ; ^
- ld a, b ; ^
- adc 0 ; Adjust for 16 bits.
- ld b, a ; ^
- ld h, b ; Load result into HL
- ld l, c ; ^
- ld bc, _SCRN1 ; Convert to tiles coords.
- addHLn16 b, c
- ENDM
- show_window: MACRO
- ld hl, rLCDC
- set LCDCB_WINONOFF, [hl]
- ENDM
- hide_window: MACRO
- ld hl, rLCDC
- res LCDCB_WINONOFF, [hl]
- ENDM
- LoadHUDTiles: MACRO
- ; Loads into 9000, tile ram starts at 8800.
- ; We load the border tiles last because we need the padding for ASCII
- ld hl, _VRAM+$1000 ; Tile RAM
- ld de, tiFnt
- ld bc, tiFntEd - tiFnt ; Get size of Font in bytes
- call Memcpy ; Copy from DE, to HL. BC amount of bytes.
- ld hl, _VRAM+$1000+TILE_BYTES
- ld de, ti_hud
- ld bc, ti_hudEd - ti_hud
- call Memcpy
- ENDM
- ; Copy visible _SCRN0 to _SCRN1
- CloneScrn:
- ld a, 18 ; Tile lines
- ld hl, _SCRN1 ; Dest
- ld de, _SCRN0 ; Source
- .CloneScrnLines
- push af
- ld bc, SCRN_X_B ; Copy n'bytes
- call Memcpy
- addHLn16 0, 12
- addDEn16 0, 12
- pop af
- dec a ; Loop for every SCRN line.
- ret z ; ^
- jr .CloneScrnLines
- Begin:
- ; Turn off the LCD
- call LCD_Off ; LCD_Off waits for VBlank already
- call ClearOAM ; Clear sprite memory (It starts filled with garbage)
- ; Clear beginning tile logo.
- ld hl, $9900
- ld a, 0
- ld bc, $33
- call Memset
- ; Now that the LCD is off so is the PPU
- ; We can now access VRAM freely.
- LoadHUDTiles
- ; Setup sprites. Load all sprites into VRAM
- ld hl, _VRAM
- ld de, ti_sprites
- ld bc, ti_spritesEd - ti_sprites
- call Memcpy
- ; Sprite 0
- xor a
- call setSpriteIndex
- ld bc, $4030 ; X, Y
- ld de, $0000 ; Tile: 00, Flags: 00
- call AddNewSprite
- ; Sprite 1
- ld a, 1
- call setSpriteIndex
- ld bc, $1616 ; X, Y
- ld de, $0000 ; Tile: 00, Flags: 00
- call AddNewSprite
- ; Copy "Press Start" to Window.
- coords 4, 7
- ld de, PStartStr ; Load 1st byte of string
- call Strcpy ; Copy from DE to HL. Until char == 0.
- ; Init display regs
- ld a, %11100100 ; Load palette, 11 10 01 00. Black,Gray,LightGray,White
- ld [rBGP], a ; Background Palette Data
- ; Sprite PAL regs
- ld [rOBP0], a
- ; Reset BG scroll X/Y
- xor a ; == ld a, 0\
- ld [rSCY], a
- ld [rSCX], a
- ; Shut off APU (Sound)
- ; bit7 is all that matters, although other bits don't do anything
- ; and 7 ends up controlling all the other channels so, meh set it to 0.
- ld [rNR52], a
- ld a, 7
- ld [rWX], a
- xor a
- ld [rWY], a
- ; Turn screen on, display the background!
- ld b, LCDCF_BGON | LCDCF_WIN9C00
- call LCD_On
- show_window
- waitjoypad PADB_START
- hide_window
- ld hl, rLCDC ; Draw sprites after pressed.
- set LCDCB_OBJONOFF, [hl] ; ^
- call WaitForVBlank
- ld hl, _SCRN0+$61 ; Start print at X:0,Y:0 tiles
- ld de, HelloStr ; Load 1st byte of string
- call Strcpy
- .mainLoop
- ; Select the "player" sprite.
- xor a
- call setSpriteIndex
- call getPadButtons
- ld d, a
- or a ; cp 0
- call nz, WaitForVBlank ; Wait for VBlank before moving sprite.
- ld a, d
- bit PADB_DOWN, a
- jr nz, .moveDown
- bit PADB_UP, a
- jr nz, .moveUp
- bit PADB_LEFT, a
- jp nz, .moveLeft
- bit PADB_RIGHT, a
- jp nz, .moveRight
- ld hl, OLDPAD ; Make sure we are not holding start.
- bit PADB_START, [hl] ; ^
- jr nz, .mainLoop ; ^
- bit PADB_START, a
- jr nz, .debugBtn
- jr .mainLoop
- ; Toggle "hud"
- .debugBtn:
- ld hl, rLCDC
- ld a, [hl]
- xor LCDCF_WINON
- ld [hl], a
- push af
- call LCD_Off
- call CloneScrn
- coords 4, 7
- ld de, PStartStr ; Load 1st byte of string
- call Strcpy ; Copy from DE to HL. Until char == 0.
- pop af
- ld b, a
- call LCD_On
- call WaitForVBlank
- call UpdateBGSpritesForHUD
- jr .mainLoop
- ; Down movement
- .moveDown
- ld d, MOVE_TILES
- ld b, 0
- ; Set walking tile
- ld c, 1
- call setSpriteTile
- call FlipSpriteX
- .moveDownCont
- ld c, MOVE_DELAY
- call DelayFrames
- ld c, 1 ; 0, 1
- call moveSprite
- ; Move again
- dec d
- jr nz, .moveDownCont
- ; Set player sprite to down-facing sprite.
- ld c, 0
- call setSpriteTile
- jp .mainLoop
- ; Up movement
- .moveUp
- ld d, MOVE_TILES
- ld b, 0
- ; Set walking up tile
- ld c, 5
- call setSpriteTile
- call FlipSpriteX
- .moveUpCont
- ld c, MOVE_DELAY
- call DelayFrames
- ld c, $FF ; 0, -1
- call moveSprite
- ; Move again
- dec d
- jr nz, .moveUpCont
- ; Set player sprite to up-facing sprite.
- ld c, 4
- call setSpriteTile
- jp .mainLoop
- ; Left movement
- .moveLeft
- ; Load right-facing sprite, flip X
- ld c, 3
- call setSpriteTile
- call FlipSpriteX
- ld d, MOVE_TILES
- ld b, $FF ; -1, 0
- .moveLeftCont
- ld c, MOVE_DELAY
- call DelayFrames
- ld c, 0
- call moveSprite
- dec d
- jr nz, .moveLeftCont
- ld c, 2
- call setSpriteTile
- jp .mainLoop
- ; Right movement
- .moveRight
- ; Load right-facing sprite, do not flip X
- ld c, 3
- call setSpriteTile
- ld d, MOVE_TILES
- ld b, $01 ; 1, 0
- call FlipSpriteX
- .moveRightCont
- ld c, MOVE_DELAY
- call DelayFrames
- ld c, 0
- call moveSprite
- ; Move again
- dec d
- jr nz, .moveRightCont
- ld c, 2
- call setSpriteTile
- jp .mainLoop
- ; RAM Variables
- Section "RAM Variables", WRAM0
- sprCount: ; Allocate sprCount ($C000)
- ds 1
- PADBTNS:
- ds 1
- OLDPAD:
- ds 1
- ; Sprites
- SECTION "Sprites", ROM0
- ti_sprites:
- INCBIN "player.2bpp"
- INCBIN "sprite.2bpp"
- ti_spritesEd:
- ; Finally, actually making the font!
- SECTION "Font", ROM0
- ti_hud:
- INCBIN "textbox_border.2bpp"
- ti_hudEd:
- tiFnt:
- INCBIN "font.2bpp" ; Tells RGBDS to copy the contents of font.chr into this ROM section.
- tiFntEd:
- Section "Text", ROM0
- WinStr:
- db "String on Window :O", 0
- PStartStr:
- db "PRESS START", 0
- HelloStr:
- db "Hello GBZ80 World!", 0 ; End with byte 0 as string return (\0 in C)
- ; db for bytes, alternatively -
- ; dw for words (16bits) and
- ; dl for longs (32bits).
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement