;;; ;; This is similar to the basics/sprite.s example, but it expands a bit on it ;; by: ;; - The ship can be moved: ;; - The movement is done through subpixels for a smoother experience. ;; - The ship's sprites are updated accordingly: resting, acceleration, full ;; speed. ;; - The ship can shoot bullets. ;; ;; The subpixel movement is largely based on: ;; https://github.com/NesHacker/PlatformerMovement. .segment "HEADER" .byte 'N', 'E', 'S', $1A ;; 2x PRG-ROM; 1x CHR-ROM .byte $02 .byte $01 ;; Horizontal mirroring, no special mapper. .byte $00 .byte $00 .segment "VECTORS" .addr nmi, reset, irq .segment "CHARS" .incbin "../../assets/space.chr" .segment "CODE" .include "../include/apu.s" .include "../include/oam.s" .include "../include/ppu.s" .include "../include/joypad.s" .include "states/game.s" .include "states/player.s" .include "states/bullets.s" .include "vectors/reset.s" .include "vectors/nmi.s" .include "vectors/irq.s" ;;; ;; This is our main subroutine, the reset procedure will call it at the very end ;; of initializing the hardware. ;;; .proc main ;; Before starting the game loop proper we initialize all our assets: load ;; the palettes, nametables and sprites for this game. jsr init_palettes jsr init_nametable jsr Player::init jsr Bullets::init cli ;; 7: allow NMI; 5: sprite size is 8x16; 4: background pattern table starts ;; at $1000. lda #%10110000 sta PPU::CONTROL ;; 4: show sprites; 3: show background; 2: show sprites in leftmost pixels ;; on the screen; 1: same as 2 but for background. lda #%00011110 sta PPU::MASK @main_game_loop: ;; The main game loop leverages the logic to different subroutines. In ;; short: first of all update the values on the joypad, then update the ;; player's movement and sprites, and finally update the bullets' ;; creation/movement. jsr Joypad::read jsr Player::Movement::update jsr Player::Sprite::update jsr Bullets::update ;; This is a hand-shake between the code on `main` and the code on the ;; `nmi`. See Game::zp_flags for more. SET_RENDER_FLAG @wait_for_render: bit Game::zp_flags bmi @wait_for_render ;; Rendering is done, we can perform another iteration of the loop! jmp @main_game_loop .endproc ;; init_palettes copies all the palettes for our game into the proper PPU ;; address. .proc init_palettes PPU_ADDR $3F00 ldx #0 @load_palettes_loop: lda palettes, x sta PPU::DATA inx cpx #$20 bne @load_palettes_loop rts palettes: ;; Background .byte $0F, $12, $22, $32 .byte $0F, $00, $28, $30 .byte $0F, $28, $16, $2D .byte $0F, $28, $16, $2D ;; Foreground .byte $0F, $00, $05, $30 .byte $0F, $00, $00, $00 .byte $0F, $00, $00, $00 .byte $0F, $00, $00, $00 .endproc ;; init_nametable loads the relevant data to the nametable that is then going to ;; be used in order to build up the background. .proc init_nametable bit PPU::STATUS ;; Big stars. WRITE_PPU_DATA $20C8, $02 WRITE_PPU_DATA $227A, $02 ;; Small stars. WRITE_PPU_DATA $20B9, $04 WRITE_PPU_DATA $21CE, $04 WRITE_PPU_DATA $21BA, $04 WRITE_PPU_DATA $22B8, $04 WRITE_PPU_DATA $22E7, $04 ;; Select palette 1 for one of the small stars, giving it a red-ish look. WRITE_PPU_DATA $23CE, %00000001 rts .endproc