Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- JOYPAD_REGISTER equ $00 ; joypad
- PAD_PORT_DPAD equ %00100000 ; select d-pad buttons
- PAD_PORT_BUTTONS equ %00010000 ; select other buttons
- PAD_OUTPUT_MASK equ %00001111 ; mask for the output buttons
- DPAD_DOWN equ 7 ; down is 7, example: cp 7 is it the down button?
- DPAD_UP equ 6 ; up is 6
- DPAD_LEFT equ 5 ; left is 5
- DPAD_RIGHT equ 4 ; right is 4
- START_BUTTON equ 3
- SELECT_BUTTON equ 2
- B_BUTTON equ 1
- A_BUTTON equ 0 ; down to here
- DPAD_DOWN_MASK equ %10000000 ; masks for the buttons
- DPAD_UP_MASK equ %01000000
- DPAD_LEFT_MASK equ %00100000
- DPAD_RIGHT_MASK equ %00010000
- START_BUTTON_MASK equ %00001000
- SELECT_BUTTON_MASK equ %00000100
- B_BUTTON_MASK equ %00000010
- A_BUTTON_MASK equ %00000001
- DIV_REGISTER equ $04 ; divide timer... read to get time, write to reset it to 0
- TIMA_REGISTER equ $05 ; main timer... freq is set in TAC reg, generates interupt when overflows
- TMA_REGISTER equ $06 ; Timer Modulo... main timer loaded with this value after it overflows
- TAC_REGISTER equ $07 ; Timer Control
- TIMER_STOP equ %00000100 ; timer halt flag... 0=stop, 1=run
- TIMER_FREQ_MASK equ %00000011 ; mask for timer frequency bits
- TIMER_FREQ_4KHz equ %00000000 ; main timer runs at 4.096 KHz
- TIMER_FREQ_262KHz equ %00000001 ; main timer runs at 262.144 KHz
- TIMER_FREQ_65KHZ equ %00000010 ; main timer runs at 65.536 KHz
- TIMER_FREQ_16KHz equ %00000011 ; main timer runs at 15.384 KHz
- IRQ_FLAG_REGISTER equ $0F ; Interrupt Flag
- VBLANK_INT equ %00000001 ; bit 0 = vblank interrupt on/off
- LCDC_INT equ %00000010 ; bit 1 = LCDC interrupt on/off
- TIMER_INT equ %00000100 ; bit 2 = Timer Overflow interrupt on/off
- SERIAL_INT equ %00001000 ; bit 3 = Serial I/O Transfer Completion interrupt on/off
- CONTROLLER_INT equ %00010000 ; bit 4 = ??
- LCDC_CONTROL equ $40 ; LCD (Graphics) Control
- BKG_DISP_FLAG equ %00000001 ; bit 0 = background tile map is on if set
- SPRITE_DISP_FLAG equ %00000010 ; bit 1 = sprites are on if set
- SPRITE_DISP_SIZE equ %00000100 ; bit 2 = sprite size (0=8x8 pixels, 1=16x8)
- BKG_MAP_LOC equ %00001000 ; bit 3 = background tile map location (0=$9800-$9bff, 1=$9c00-$9fff)
- TILES_LOC equ %00010000 ; bit 4 = tile data location (0=$8800-$97ff, 1=$8000-$8fff)
- WINDOW_DISP_FLAG equ %00100000 ; bit 5 = window tile map is on if set
- WINDOW_MAP_LOC equ %01000000 ; bit 6 = window tile map location (0=$9800-$9bff, 1=$9c00-9fff)
- DISPLAY_FLAG equ %10000000 ; bit 7 = LCD display on if set
- LCDC_STATUS equ $41 ; LCDC Status
- DISP_CYCLE_MODE equ %00000011 ; mask for the display cycle mode bits
- VBLANK_MODE equ %00000000 ; system is in vertical blanking interval
- HBLANK_MODE equ %00000001 ; system is in a horizontal blanking interval
- SPRITE_MODE equ %00000010 ; system is reading sprite RAM
- LCD_TRANSFER equ %00000011 ; system is transfering data to the LCD driver
- SCROLL_BKG_Y equ $42 ; vertical scroll position of background tile map
- SCROLL_BKG_X equ $43 ; horizontal scroll position of background tile map
- LCDC_LY_COUNTER equ $44 ; increments every scan line (0..143 = display, 144-153 = vblank)
- LY_COMPARE equ $45 ; ??
- DMA_REGISTER equ $46 ; DMA Transfer and Start Address
- PALETTE_BKG equ $47 ; palette data for background tile map
- PALETTE_SPRITE_0 equ $48 ; sprite palette 0 data
- PALETTE_SPRITE_1 equ $49 ; sprite palette 1 data
- POS_WINDOW_Y equ $4A ; window tile map Y position
- POS_WINDOW_X equ $4B ; window tile map X position
- INTERRUPT_ENABLE equ $ff ; Interrupt Enable
- ; $ff80 to $fffe is 128 bytes of internal RAM
- STACK_TOP equ $fff4 ; put the stack here
- ; video ram display locations
- TILES_MEM_LOC_0 equ $8800 ; tile map tiles only
- TILES_MEM_LOC_1 equ $8000 ; tile maps and sprite tiles
- MAP_MEM_LOC_0 equ $9800 ; background and window tile maps
- MAP_MEM_LOC_1 equ $9c00 ; (select which uses what mem loc in LCDC_CONTROL register)
- SPRITE_ATTRIB_MEM_LOC equ $fe00 ; OAM memory (sprite attributes)
- ; sprite attribute flags
- SPRITE_FLAGS_PAL equ %00010000 ; palette (0=sprite pal 0, 1=sprite pal 1)
- SPRITE_FLAGS_XFLIP equ %00100000 ; sprite is horizontal flipped
- SPRITE_FLAGS_YFLIP equ %01000000 ; sprite is vertical flipped
- SPRITE_FLAGS_PRIORITY equ %10000000 ; sprite display priority (0=on top bkg & win, 1=behind bkg & win)
- ;-------------------------------------------------------------------------
- ; start of the game rom (address 0000)
- ;-------------------------------------------------------------------------
- SECTION "rst 00", ROM0 [$00]
- rst $38
- SECTION "rst 08", ROM0 [$08]
- rst $38
- SECTION "rst 10", ROM0 [$10]
- rst $38
- SECTION "rst 18", ROM0 [$18]
- rst $38
- SECTION "rst 20", ROM0 [$20]
- rst $38
- SECTION "rst 28", ROM0 [$28]
- rst $38
- SECTION "rst 30", ROM0 [$30]
- rst $38
- SECTION "rst 38", ROM0 [$38]
- rst $38
- ; NOTE: the hardware requires the interrupt jumps to be at these addresses
- SECTION "VBlank_IRQ_Jump",HOME[$0040]
- ; Vertical Blanking interrupt
- jp VBlankFunc
- SECTION "LCDC_IRQ_Jump",HOME[$0048]
- ; LCDC Status interrupt (can be set for H-Blanking interrupt)
- rst $38
- SECTION "Timer_Overflow_IRQ_Jump",HOME[$0050]
- ; Main Timer Overflow interrupt
- reti
- SECTION "Serial_IRQ_Jump",HOME[$0058]
- ; Serial Transfer Completion interrupt
- reti
- SECTION "Joypad_IRQ_Jump",HOME[$0060]
- ; Joypad Button Interrupt
- reti
- SECTION "GameBoy_Header_Start",HOME[$0100]
- GameBoyHeaderStart::
- ; begining of Game Boy game header
- nop ; cpu cycle once
- jp title_stuff ; goto beginning of game code
- ; Game Boy standard header... DO NOT CHANGE!
- db $CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83,$00,$0C,$00,$0D
- db $00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6,$DD,$DD,$D9,$99
- db $BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F,$BB,$B9,$33,$3E
- db "SILVER " ; game name (must be 16 bytes)
- db $00,$00,$00 ; unused
- db $00 ; cart type
- db $00 ; ROM Size (32 k)
- db $00 ; RAM Size (0 k)
- db $00,$00 ; maker ID
- db $01 ; Version =1
- db $DA ; Complement check (Important)
- db $ff,$ff ; checksum, calculate
- SECTION "Game_Code_Start",HOME
- ; begining of game code
- ; this routine sets up some registers and values, and starts the game
- title_stuff::
- ld sp, STACK_TOP ; put stack where it wants it
- ld a, VBLANK_INT ; get the vblank int
- ldh [INTERRUPT_ENABLE], a ; load it into the interrupt enable
- sub a ; default accumulator value is 1, apparently
- ldh [LCDC_STATUS], a ; 0
- ldh [LCDC_CONTROL], a ; 0
- ld a, 0 ; why do we have this?
- ld [vblank_flag], a ; reset vblank flag
- ld bc, TitleScreenData ; load our title screen into here
- call LoadTitleScreen ; load the title screen, uses the bc register to load
- ; setup the screen and other formats
- ld a, DISPLAY_FLAG | BKG_DISP_FLAG | SPRITE_DISP_FLAG | TILES_LOC | WINDOW_MAP_LOC ; load this
- ldh [LCDC_CONTROL],a ; into the LCD control
- call wait_for_key ; wait for a key to be pressed
- ; this routine loads our title screen and shows it.
- LoadTitleScreen::
- ld hl, TILES_MEM_LOC_1 ; load the tiles to tiles bank 1
- ld de, 4 * 16
- ld d, $10 ; 16 bytes per tile
- ld e, $05 ; number of tiles to load
- .load_title_loop
- ; only write during
- ldh a, [LCDC_STATUS] ; get the status
- and SPRITE_MODE ; don't write during sprite and transfer modes
- jr nz, .load_title_loop ; ^ loop
- ld a, [bc] ; get the next value from the source
- ld [hli], a ; load the value to the destination, incrementing dest. ptr
- inc bc ; increment the source ptr
- ; now loop <DE> times
- ; eli5 wait for d/e = 0
- dec d
- jp nz, .load_title_loop ; is d = 0?
- dec e
- jp nz, .load_title_loop ; is e = 0?
- ret ; make sure we return back
- ; this function waits for A to be pressed
- ; when A is pressed, it starts the game, loads the sprites, and the main stuff
- ; make sure we add a corporate logo too for "xetoro" lol
- wait_for_key::
- call ReadJoypad ; read the joypad
- ld a, [joypad_down] ; get the button pressed
- bit A_BUTTON, a ; was it the a button?
- jr z, .key_loop ; if not, keep looping
- ld a, [vblank_flag] ; get the vblank flag
- cp 0 ; check the vblank flag
- jr z, wait_for_key ; are we in vblank? go back to waiting for a key...
- jp start ; otherwise, if the a button was pressed, start the game
- .key_loop
- jp wait_for_key ; looping function
- ; function to wait for vblank
- ; it waits for vblank then rets
- WaitVBL::
- ld a, [vblank_flag] ; get the vblank flag
- cp 0 ; are we in vblank?
- jr z, WaitVBL ; no, keep looping
- start::
- ld a, 16
- ldh [SCROLL_BKG_X], a ; background map will start at 16,16
- ldh [SCROLL_BKG_Y], a
- ; load the tiles
- ld bc, TileData
- call LoadTiles
- ; load the background map
- ld bc, BkgMapData
- call LoadMapToBkg
- call InitSprites ; clear sprites
- call InitPalettes ; init the palettes
- ; init player sprite
- ld a, $40
- ld [spaceship_xpos], a
- ld [spaceship_ypos], a
- ld a, 2
- ld [spaceship_tile], a
- ld a, 0
- ld [spaceship_flags], a
- ; allow interrupts to start occuring
- ei
- jp Game_Loop
- ; main game loop
- Game_Loop::
- ; don't do a frame update unless we have had a vblank
- ld a, [vblank_flag]
- cp 0
- jp z, .end_game_loop
- ; get this frame's joypad info
- call ReadJoypad
- ; adjust sprite due to d-pad presses
- call MoveSpaceship ; yeah I know, I'm dumb
- ; reset vblank flag
- ld a, 0
- ld [vblank_flag], a
- .end_game_loop
- ; time to loop!
- jp Game_Loop
- ;-----------------------------------------------------------------------
- ; copy a block of data
- ;
- ; in: de - destination ptr
- ; hl - source ptr
- ; b - number of bytes to copy
- ;-----------------------------------------------------------------------
- CopyBlock::
- push af ; save af
- .copy_block_loop
- ld a, [hli] ; inc hl
- ld [de], a ; load it into de
- inc de ; inc that
- dec b ; dec that
- jr nz, .copy_block_loop ; then loop
- pop af ; restore af and return
- ret ;
- ;------------------------------------------
- ; init the local copy of the sprites
- ;------------------------------------------
- InitSprites::
- ld hl, $c000 ; my sprites are at $c000
- ld b, 40*4 ; 40 sprites, 4 bytes per sprite
- ld a, $ff
- .init_sprites_loop
- ld [hli], a
- dec b
- jr nz, .init_sprites_loop
- ret
- ;----------------------------------------------------
- ; load the tiles from ROM into the tile video memory
- ;
- ; IN: bc = address of tile data to load
- ;----------------------------------------------------
- LoadTiles::
- ld hl, TILES_MEM_LOC_1 ; load the tiles to tiles bank 1
- ld de, 4 * 16
- ld d, $10 ; 16 bytes per tile
- ld e, $05 ; number of tiles to load
- .load_tiles_loop
- ; only write during
- ldh a, [LCDC_STATUS] ; get the status
- and SPRITE_MODE ; don't write during sprite and transfer modes
- jr nz, .load_tiles_loop
- ld a, [bc] ; get the next value from the source
- ld [hli], a ; load the value to the destination, incrementing dest. ptr
- inc bc ; increment the source ptr
- ; now loop de times
- dec d
- jp nz, .load_tiles_loop
- dec e
- jp nz, .load_tiles_loop
- ret
- ;----------------------------------------------------
- ; load the tile map to the background
- ;
- ; IN: bc = address of map to load
- ;----------------------------------------------------
- LoadMapToBkg::
- ld hl, MAP_MEM_LOC_0 ; load the map to map bank 0
- ld d, $00 ; 256 bytes per "block"
- ld e, $04 ; 4 blocks (32x32 tiles, 1024 bytes)
- .load_map_loop
- ; only write during
- ldh a, [LCDC_STATUS] ; get the status
- and SPRITE_MODE ; don't write during sprite and transfer modes
- jr nz, .load_map_loop
- ld a, [bc] ; get the next value from the source
- ld [hli], a ; load the value to the destination, incrementing dest. ptr
- inc bc ; increment the source ptr
- ; now loop de times
- dec d
- jp nz, .load_map_loop
- dec e
- jp nz, .load_map_loop
- ret
- ;----------------------------------------------------
- ; init the palettes to basic
- ;----------------------------------------------------
- InitPalettes::
- ld a, %10010011 ; set palette colors
- ; load it to all the palettes
- ldh [PALETTE_BKG], a
- ldh [PALETTE_SPRITE_0], a
- ldh [PALETTE_SPRITE_1], a
- ret
- ;-----------------------------------------------------------------------
- ; read the joypad
- ;
- ; output:
- ; This loads two variables:
- ; joypad_held - what buttons are currently held
- ; joypad_down - what buttons went down since last joypad read
- ;-----------------------------------------------------------------------
- ReadJoypad::
- ; get the d-pad buttons
- ld a, PAD_PORT_DPAD ; select d-pad
- ldh [JOYPAD_REGISTER], a ; send it to the joypad
- ldh a, [JOYPAD_REGISTER]
- ldh a, [JOYPAD_REGISTER]
- ldh a, [JOYPAD_REGISTER]
- ldh a, [JOYPAD_REGISTER]
- ldh a, [JOYPAD_REGISTER]
- ldh a, [JOYPAD_REGISTER] ; get the result back (takes a few cycles)
- cpl ; bit-flip the result
- ; ld b, a
- and PAD_OUTPUT_MASK ; mask out the output bits
- swap a ; put the d-pad button results to top nibble
- ld b, a ; and store it
- ; get A / B / SELECT / START buttons
- ld a, PAD_PORT_BUTTONS ; select buttons
- ldh [JOYPAD_REGISTER], a ; send it to the joypad
- ldh a, [JOYPAD_REGISTER]
- ldh a, [JOYPAD_REGISTER]
- ldh a, [JOYPAD_REGISTER]
- ldh a, [JOYPAD_REGISTER]
- ldh a, [JOYPAD_REGISTER]
- ldh a, [JOYPAD_REGISTER] ; get the result back (takes even more cycles?)
- cpl ; bit-flip the result
- and PAD_OUTPUT_MASK ; mask out the output bits
- or b ; add it to the other button bits
- ld b, a ; put it back in c
- ; calculate the buttons that went down since last joypad read
- ld a, [joypad_held] ; grab last button bits
- cpl ; invert them
- and b ; combine the bits with current bits
- ld [joypad_down], a ; store just-went-down button bits
- ld a, b
- ld [joypad_held], a ; store the held down button bits
- ld a, $30 ; reset joypad
- ldh [JOYPAD_REGISTER],A
- ret ; done
- ;
- ; some random text that will be incorporated into the game later
- ; or maybe not, who knows. this is just here for later when we get a printtext routine going
- ;
- IGText::
- db "Welcome to Silver!", 0
- ;---------------------------------------------------
- ; my vblank routine - do all graphical changes here
- ; while the display is not drawing
- ;---------------------------------------------------
- VBlankFunc::
- di ; disable interrupts
- push af
- ; increment my little timer
- ld a, [ScrollTimer] ; get the scroll timer
- inc a ; increment it
- ld [ScrollTimer], a
- ; is it time to scroll yet?
- and %00000001
- jr nz, .vblank_sprite_DMA ; only scroll ever other vblank
- .vblank_do_scroll
- ld c, 3 ; scroll 3 pixels
- ld a, [scrl_dir_flag]
- cp 0
- jr z, .vblank_scroll_up
- call ScrollScreenDown
- jr .vblank_scrolled_down
- .vblank_scroll_up
- call ScrollScreenUp
- .vblank_scrolled_down
- ; load the sprite attrib table to OAM memory
- .vblank_sprite_DMA
- ld a, $c0 ; dma from $c000 (where I have my local copy of the attrib table)
- ldh [DMA_REGISTER], a ; start the dma
- ld a, $28 ; wait for 160 microsec (using a loop)
- .vblank_dma_wait
- dec a
- jr nz, .vblank_dma_wait
- ld hl, SPRITE_ATTRIB_MEM_LOC
- ; set the vblank occured flag
- ld a, 1
- ld [vblank_flag], a
- pop af
- ei ; enable interrupts
- reti ; and done
- MoveSpaceship::
- push af
- ; check buttons for d-pad presses
- .check_for_up
- ld a, [joypad_held]
- bit DPAD_UP, a
- jp z, .check_for_down ; if button not pressed then done
- ; up was held down
- .check_for_upright
- ; is right also held?
- ld a, [joypad_held]
- bit DPAD_RIGHT, a
- jp z, .check_for_upleft
- ; up + right held, so sprite needs to be diagonal
- ld a, 1 ; diagonal sprite is tile 1
- ld [spaceship_tile], a
- ld a, 0 ; flags are no x or y flip
- ld [spaceship_flags], a
- jp .adjust_up_pos
- .check_for_upleft
- ; is left also held?
- ld a, [joypad_held]
- bit DPAD_LEFT, a
- jp z, .set_up_only
- ; up + left held, so sprite needs to be diagonal
- ld a, 1 ; diagonal sprite is tile 1
- ld [spaceship_tile], a
- ld a, SPRITE_FLAGS_XFLIP ; sprite should be x flipped
- ld [spaceship_flags], a
- jp .adjust_up_pos
- .set_up_only
- ; only up was held, so sprite needs to be up
- ld a, 0 ; vertical sprite is tile 0
- ld [spaceship_tile], a
- ld a, 0
- ld [spaceship_flags], a
- .adjust_up_pos
- ; adjust the sprite's position
- ld a, [ScrollTimer] ; only move sprite every 2nd vblank
- and %00000001
- jr nz, .check_for_left
- ; move sprite up a pixel
- ld a, [spaceship_ypos]
- dec a
- ld [spaceship_ypos], a
- ; don't check down, since up + down should never occur
- jp .check_for_left
- .check_for_down
- ld a, [joypad_held]
- bit DPAD_DOWN, a
- jp z, .check_for_left ; if button not pressed then done
- ; down was held down
- .check_for_downright
- ; is right also held?
- ld a, [joypad_held]
- bit DPAD_RIGHT, a
- jp z, .check_for_downleft
- ; down + right held, so sprite needs to be diagonal
- ld a, 1 ; diagonal sprite is tile 1
- ld [spaceship_tile], a
- ld a, SPRITE_FLAGS_YFLIP ; y flip the sprite
- ld [spaceship_flags], a
- jp .adjust_down_pos
- .check_for_downleft
- ; is left also held?
- ld a, [joypad_held]
- bit DPAD_LEFT, a
- jp z, .set_down_only
- ; down + left held, so sprite needs to be diagonal
- ld a, 1 ; diagonal sprite is tile 1
- ld [spaceship_tile], a
- ld a, SPRITE_FLAGS_XFLIP + SPRITE_FLAGS_YFLIP ; sprite should be x and y flipped
- ld [spaceship_flags], a
- jp .adjust_down_pos
- .set_down_only
- ; only down was held, so sprite needs to be down
- ld a, 0 ; vertical sprite is tile 0
- ld [spaceship_tile], a
- ld a, SPRITE_FLAGS_YFLIP
- ld [spaceship_flags], a
- .adjust_down_pos
- ; adjust the sprite's position
- ld a, [ScrollTimer] ; only move sprite every 2nd vblank
- and %00000001
- jr nz, .check_for_left
- ; move sprite up a pixel
- ld a, [spaceship_ypos]
- inc a
- ld [spaceship_ypos], a
- .check_for_left
- ld a, [joypad_held]
- bit DPAD_LEFT, a
- jp z, .check_for_right ; if button not pressed then done
- ; left was pressed
- .check_left_andUpOrDown
- ld a, [joypad_held]
- and DPAD_UP_MASK + DPAD_DOWN_MASK
- jp nz, .adjust_left_pos ; if up or down was pressed, then we already set the sprite attribs
- ; sprite needs to be horizontal
- ld a, 2 ; horizontal sprite is tile 2
- ld [spaceship_tile], a
- ld a, SPRITE_FLAGS_XFLIP
- ld [spaceship_flags], a
- .adjust_left_pos
- ld a, [ScrollTimer] ; only move sprite every 2nd vblank
- and %00000001
- jr nz, .done_checking_dpad
- ; move sprite left one pixel
- ld a, [spaceship_xpos]
- dec a
- ld [spaceship_xpos], a
- jp .done_checking_dpad ; if left was pressed, don't check right
- .check_for_right
- ld a, [joypad_held]
- bit DPAD_RIGHT, a
- jp z, .done_checking_dpad ; if button not pressed then done
- ; right was pressed
- .check_right_andUpOrDown
- ld a, [joypad_held]
- and DPAD_UP_MASK + DPAD_DOWN_MASK
- jp nz, .adjust_right_pos ; if up or down was pressed, then we already set the sprite attribs
- ; sprite needs to be horizontal
- ld a, 2 ; horizontal sprite is tile 2
- ld [spaceship_tile], a
- ld a, 0
- ld [spaceship_flags], a
- .adjust_right_pos
- ld a, [ScrollTimer] ; only move sprite every 2nd vblank
- and %00000001
- jr nz, .done_checking_dpad
- ; move sprite left one pixel
- ld a, [spaceship_xpos]
- inc a
- ld [spaceship_xpos], a
- jp .done_checking_dpad ; if left was pressed, don't check right
- .done_checking_dpad
- ld a, [joypad_down]
- bit A_BUTTON, a
- jr z, .check_b_button
- .check_b_button
- ld a, [joypad_down]
- bit B_BUTTON, a
- jr z, .done_move_ship
- ld a, [scrl_dir_flag]
- xor 1
- ld [scrl_dir_flag], a ; toggle the scroll direction flag
- .done_move_ship
- pop af
- ret
- ;-------------------------------------------------------------
- ; scroll the screen up a few pixels, adding new tiles to the
- ; bottom of the screen map as necessary
- ;
- ; in: c = # pixels to scroll
- ;-------------------------------------------------------------
- ScrollScreenUp::
- push af
- push bc
- push de
- push hl
- ; get the number of pixels we have scrolled since the last tile load
- ld a, [y_scrl_accum]
- add a, c
- cp 8 ; has the accum gone past 7?
- jr c, .scroll_screen_up_done ; no, we don't need to add any tiles
- ; yes, we need to add some tiles to the bottom of the screen
- .scroll_screen_up_tiles_loop
- ; calc the line we need to add tiles to
- push af ; save the loop variable
- ldh a, [SCROLL_BKG_Y] ;get the current y screen position
- srl a
- srl a
- srl a ; divide by 8 (get tile pos from pixel pos)
- add a, 20 ; screen is 18 tiles hi, so add tiles 20 lines down from screen pos
- cp 32 ; did we go past the end of the bkg map?
- jr c, .scroll_screen_up_add_tiles ; no, we have the tile line we need
- ; yes, we need to wrap
- sub 32
- .scroll_screen_up_add_tiles
- ld b, a ; store line number for input to routine
- ld a, [world_y] ; get our world screen position (lo byte)
- ld d, a
- ld a, [world_y+1] ; hi byte
- srl a
- rr d ; left shift the hi and low bytes
- srl a
- rr d ; left shift the hi and low bytes
- srl a
- rr d ; left shift the hi and low bytes - div by 8!
- ld a, d ; the low byte hold the interesting data after ths shift
- add a, 20 ; go down 20 tiles to tile we want to add
- cp 64 ; world is 64 tiles tall
- jr c, .scroll_screen_up_wrap_world
- sub 64 ; wrap
- .scroll_screen_up_wrap_world
- ld e, a
- ld d, 0
- sla e
- rl d ; shift de up one bit
- sla e
- rl d ; shift de up one bit
- sla e
- rl d ; shift de up one bit
- sla e
- rl d ; shift de up one bit
- sla e
- rl d ; shift de up one bit - multiply world_y by 32
- ld hl, WorldMap ; get the top of the world map
- add hl, de ; set address of line to load
- call AddWorldTilesLine ; add the line of tiles!
- pop af ; restore the loop variable
- sub 8 ; did one tile line (8 pixels)
- cp 7 ; still greater than 7?
- jr nc, .scroll_screen_up_tiles_loop ; yes, do another line of tiles!
- .scroll_screen_up_done
- ; update the y scroll accumulator and the world pos
- ld [y_scrl_accum], a
- ld a, [world_y+1] ; get world y pos hi byte
- ld b, a
- ld a, [world_y] ; lo byte
- add a, c ; adjust world pos
- jr nc, .scroll_screen_up_nocarry
- inc b ; carry flag... increase hi byte
- .scroll_screen_up_nocarry
- ld d, a
- ld a, b ; get hi byte of world y
- cp $02 ; less than hi byte of world height (64 tiles * 8 pixels/byte = 512)?
- jr c, .scroll_screen_up_worldwrap
- ld a, d
- cp $00 ; lo byte of world x < lo byte of 512?
- jr c, .scroll_screen_up_worldwrap
- ; world y is greater than world map y... we need to wrap
- ld b, 0 ; clear hi byte, leave lo byte alone (now $20x becomes $00x)
- .scroll_screen_up_worldwrap
- ld a, d
- ld [world_y], a
- ld a, b
- ld [world_y+1], a
- ; update the background scroll plane scroll
- ldh a, [SCROLL_BKG_Y]
- add a, c
- ldh [SCROLL_BKG_Y], a
- pop hl
- pop de
- pop bc
- pop af
- ret
- ;-------------------------------------------------------------
- ; scroll the screen down a few pixels, adding new tiles to the
- ; top of the screen map as necessary
- ;
- ; in: c = # pixels to scroll
- ;-------------------------------------------------------------
- ScrollScreenDown::
- push af
- push bc
- push de
- push hl
- ; get the number of pixels we have scrolled since the last tile load
- ld a, [y_scrl_accum]
- sub c
- cp 8 ; has the accum gone below 0?
- jr c, .scroll_screen_down_done ; no, we don't need to add any tiles
- ; yes, we need to add some tiles to the top of the screen
- .scroll_screen_down_tiles_loop
- ; calc the line we need to add tiles to
- push af ; save the loop variable
- ldh a, [SCROLL_BKG_Y] ;get the current y screen position
- srl a
- srl a
- srl a ; divide by 8 (get tile pos from pixel pos)
- sub 2 ; add tiles to line two tiles off the top of the screen
- cp 32 ; did we go past the end of the bkg map?
- jr c, .scroll_screen_down_add_tiles ; no, we have the tile line we need
- ; yes, we need to wrap
- add a, 32
- .scroll_screen_down_add_tiles
- ld b, a ; store line number for input to routine
- ld a, [world_y] ; get our world screen position (lo byte)
- ld d, a
- ld a, [world_y+1] ; hi byte
- srl a
- rr d ; left shift the hi and low bytes
- srl a
- rr d ; left shift the hi and low bytes
- srl a
- rr d ; left shift the hi and low bytes - div by 8!
- ld a, d ; the low byte hold the interesting data after ths shift
- sub 2 ; go up two tiles to tile we want to add
- cp 64 ; world is 64 tiles tall
- jr c, .scroll_screen_down_wrap_world
- add a, 64 ; wrap
- .scroll_screen_down_wrap_world
- ld e, a
- ld d, 0
- sla e
- rl d ; shift de up one bit
- sla e
- rl d ; shift de up one bit
- sla e
- rl d ; shift de up one bit
- sla e
- rl d ; shift de up one bit
- sla e
- rl d ; shift de up one bit - multiply world_y by 32
- ld hl, WorldMap ; get the top of the world map
- add hl, de ; set address of line to load
- call AddWorldTilesLine ; add the line of tiles!
- pop af ; restore the loop variable
- add a, 8 ; did one tile line (8 pixels)
- cp 8 ; still less than zero (greater than 8)?
- jr nc, .scroll_screen_down_tiles_loop ; yes, do another line of tiles!
- .scroll_screen_down_done:
- ; update the y scroll accumulator and the world pos
- ld [y_scrl_accum], a
- ld a, [world_y+1] ; get world y pos hi byte
- ld b, a
- ld a, [world_y] ; lo byte
- sub c ; adjust world pos
- jr nc, .scroll_screen_down_nocarry
- dec b ; carry flag... increase hi byte
- .scroll_screen_down_nocarry
- ld d, a
- ld a, b ; get hi byte of world y
- cp $02 ; less than hi byte of world height (64 tiles * 8 pixels/byte = 512)?
- jr c, .scroll_screen_down_worldwrap
- ld a, d
- cp $00 ; lo byte of world x < lo byte of 512?
- jr c, .scroll_screen_down_worldwrap
- ; world y is greater than world map y... we need to wrap
- ld b, 1 ; set hi byte to 1 (now $fffx becomes $01fx)
- .scroll_screen_down_worldwrap
- ld a, d
- ld [world_y], a
- ld a, b
- ld [world_y+1], a
- ; update the background scroll plane scroll
- ldh a, [SCROLL_BKG_Y]
- sub c
- ldh [SCROLL_BKG_Y], a
- pop hl
- pop de
- pop bc
- pop af
- ret
- ;-----------------------------------------------------
- ; add a line of tiles to the background map
- ;
- ; in: b = bkg map line number to add tiles to
- ; hl = ptr to 1st tile to add
- ;-----------------------------------------------------
- AddWorldTilesLine::
- push af
- push bc
- push de
- ldh a, [SCROLL_BKG_X] ; get the horiz bkg map scroll value
- srl a
- srl a
- srl a ; divide by 8 (convert pixel value to tile value)
- sub 2 ; go left two more tiles
- cp 32 ; did we go past 0?
- jr c, .add_tiles_line_get_vram_addr ; no
- ; yes, wrap us back
- add a, 32
- .add_tiles_line_get_vram_addr
- ld c, a ; store the x tile number
- ld e, b ; get the bkg line number
- ld d, 0
- sla e
- rl d ; shift de up one bit
- sla e
- rl d ; shift de up one bit
- sla e
- rl d ; shift de up one bit
- sla e
- rl d ; shift de up one bit
- sla e
- rl d ; shift de up one bit - multiply de by 32
- ld a, e
- add a, c
- ld e, a ; add the xpos to de
- push hl
- ld hl, MAP_MEM_LOC_0 ; load to map 0
- add hl, de ; hl now points at map memory to load to
- ld d, h
- ld e, l ; store it in de
- pop hl ; and restore hl = world tile ptr
- ld b, 24 ; load 24 tiles
- .add_tiles_line_loop
- ; only write during
- ; ldh a, [LCDC_STATUS] ; get the status
- ; and SPRITE_MODE ; don't write during sprite and transfer modes
- ; jr nz, add_tiles_line_loop
- ld a, [hli]
- ld [de], a ; load a tile ref
- inc c ; inc the x pos
- ld a, c
- cp 32
- jr c, .add_tiles_line_loop_end
- sub 32
- ld c, a ; wrap back
- ld a, e
- sub 32
- ld e, a
- cp 32
- jr c, .add_tiles_line_loop_end
- dec d ; subtract 32 from de
- .add_tiles_line_loop_end
- ld a, e ; increment de
- inc e
- jr nz, .add_tiles_line_incDE
- inc d ; e became $00, so we need to carry the add to top byte
- .add_tiles_line_incDE
- dec b
- jp nz, .add_tiles_line_loop
- pop de
- pop bc
- pop af
- ret
- ; tiles are here
- TileData:
- INCLUDE "tgTiles.dat" ; spaceship etc
- TitleScreenData:
- INCLUDE "titleScreen.dat" ; title screen
- ; map is here
- WorldMap:
- BkgMapData
- INCLUDE "tgMap.dat"
- ;-------------------------------------------------------------------------
- ; Internal RAM... store dynamic data here
- ;-------------------------------------------------------------------------
- SECTION "RAM_Start_Sprites",BSS[$C000]
- ; local version of sprite attrib table
- spaceship_ypos:
- ds 1
- spaceship_xpos:
- ds 1
- spaceship_tile:
- ds 1
- spaceship_flags:
- ds 1
- SECTION "RAM_Other_Variables",BSS[$C0A0]
- ; other variables
- ; joypad values
- joypad_held:
- ds 1 ; what buttons are currently held
- joypad_down:
- ds 1 ; what buttons went down since last joypad read
- ; scroll values
- world_x:
- ds 2
- world_y:
- ds 2
- x_scrl_accum:
- ds 1
- y_scrl_accum:
- ds 1
- ; bullets (16 of them, 2 bytes for each)
- ; 1st byte = orientation (3 bits) - if $ff, this bullet is unused
- ; 2nd byte = time left to live (in vblanks)
- bullet_data:
- ds 32
- ; frame timing
- vblank_flag:
- ds 1 ; set if a vblank occured since last pass through game loop
- ; scroll direction flag
- scrl_dir_flag:
- ds 1
- ; temp variables
- ScrollTimer:
- ds 1 ; temp variable for slowing down scroll speed
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement