include "nesdefs.dasm" ;;;;; VARIABLES seg.u ZEROPAGE org $0 ScrollPos word ; used during NMI Rand byte ; random number Temp1 byte ; temporary ; OAM sprite buffer SpriteBuf equ $200 ;;;;; NES CARTRIDGE HEADER NES_HEADER 0,2,1,1 ; mapper 0, 2 PRGs, 1 CHR, horiz. mirror ;;;;; START OF CODE Start: NES_INIT ; set up stack pointer, turn off PPU jsr WaitSync jsr WaitSync jsr ClearRAM jsr WaitSync ;wait for VSYNC jsr SetPalette ;set colors jsr FillVRAM ;set PPU RAM jsr WaitSync ;wait for VSYNC (and PPU warmup) jsr InitSprites lda #0 sta PPU_ADDR sta PPU_ADDR ;PPU addr = 0 sta PPU_SCROLL sta PPU_SCROLL ;scroll = 0 lda #MASK_BG|MASK_SPR sta PPU_MASK ; enable rendering lda #CTRL_NMI sta PPU_CTRL ; enable NMI .endless jmp .endless ;endless loop ; set palette colors SetPalette: subroutine PPU_SETADDR $3f00 ldy #0 .loop: lda Palette,y ; lookup byte in ROM sta PPU_DATA ; store byte to PPU data iny ; Y = Y + 1 cpy #32 ; is Y equal to 32? bne .loop ; not yet, loop rts ; return to caller ; fill video RAM FillVRAM: subroutine PPU_SETADDR $2000 ldy #$10 ldx #0 .loop: stx PPU_DATA ; X -> PPU data port inx ; X = X + 1 bne .loop ; repeat until 256 bytes dey ; Y = Y - 1 bne .loop ; repeat until Y is 0 rts ; return to caller ; initialize OAM data InitSprites: subroutine lda #1 ; A = 1 ldx #0 ; X = 0 .loop sta SpriteBuf,x ; store to OAM buffer jsr NextRandom ; get next random number inx ; X = X + 1 bne .loop ; loop until X wraps rts ; return to caller ; move sprite data MoveSprites: subroutine lda #1 ; A = 1 ldx #0 ; X = 0 .loop sta Temp1 ; save A and #3 ; keep lower 2 bits clc ; clear carry before add adc SpriteBuf,x ; add to sprite buffer sta SpriteBuf,x ; store in sprite buffer lda Temp1 ; restore A jsr NextRandom ; get next random number inx ; X = X + 1 bne .loop ; loop until X wraps rts ; return to caller ;;;;; COMMON SUBROUTINES include "nesppu.dasm" ;;;;; INTERRUPT HANDLERS NMIHandler: ; save registers SAVE_REGS ; update scroll position (must be done after VRAM updates) inc ScrollPos ; increment low byte bne .noinc ; Z flag set if wrapped to 0 inc ScrollPos+1 ; increment high byte .noinc ; store X and Y scroll position lda ScrollPos ; A -> low byte sta PPU_SCROLL ; set horiz scroll lda #0 ; A -> zero sta PPU_SCROLL ; set vert scroll ; store 8th bit into name table selector ; name table A or B ($2000 or $2400) lda ScrollPos+1 ; load high byte and #1 ; select its low bit ora #CTRL_NMI ; set rest of bits sta PPU_CTRL ; load sprites lda #$02 ; page 2 ($0200) sta PPU_OAM_DMA ; start DMA transfer ; move sprites jsr MoveSprites ; move sprites ; restore registers, return from interrupt RESTORE_REGS rti ;;;;; CONSTANT DATA Palette: hex 1f ;screen color hex 01112100 ;background 0 hex 02122200 ;background 1 hex 02112100 ;background 2 hex 01122200 ;background 3 hex 19293900 ;sprite 0 hex 1a2a3a00 ;sprite 1 hex 1b2b3b00 ;sprite 2 hex 1c2c3c ;sprite 3 ;;;;; CPU VECTORS NES_VECTORS ;;;;; TILE SETS org $10000 incbin "jroatch.chr" incbin "jroatch.chr"