.segment "HEADER" INES_MAPPER = 0 INES_MIRROR = 1 INES_SRAM = 0 .byte 'N', 'E', 'S', $1A .byte $02 .byte $01 .byte INES_MIRROR .byte (INES_MAPPER & %11110000) .byte $0, $0, $0, $0, $0, $0, $0, $0 .segment "TILES" .incbin "background.chr" .incbin "sprite.chr" .segment "VECTORS" .word nmi .word reset .word irq .segment "CODE" reset: sei lda #0 sta $2000 sta $2001 sta $4015 sta $4010 lda #40 sta $4017 cld ldx #$FF txs bit $2002 : bit $2002 bpl :- lda #0 ldx #0 : sta $0000, X sta $0100, X sta $0200, X sta $0300, X sta $0400, X sta $0500, X sta $0600, X sta $0700, X inx bne :- lda #255 ldx #0 : sta oam, X inx inx inx inx bne :- : bit $2002 bpl :- lda #%10001000 sta $2000 jmp main .segment "ZEROPAGE" nmi_lock: .res 1 nmi_count: .res 1 nmi_ready: .res 1 nmt_update_len: .res 1 scroll_x: .res 1 scroll_y: .res 1 scroll_nmt: .res 1 temp: .res 1 .segment "BSS" nmt_update: .res 256 palette: .res 32 .segment "OAM" oam: .res 256 .segment "CODE" nmi: pha txa pha tya pha lda nmi_lock beq :+ jmp @nmi_end : lda #1 sta nmi_lock lda nmi_ready bne :+ jmp @ppu_update_end : cmp #2 bne :+ lda #%00000000 sta $2001 ldx #0 stx nmi_ready jmp @ppu_update_end : ldx #0 stx $2003 lda #>oam sta $4014 lda #%10001000 sta $2000 lda $2002 lda #$3F sta $2006 stx $2006 ldx #0 : lda palette, X sta $2007 inx cpx #32 bcc :- ldx #0 cpx nmt_update_len bcs @scroll @nmt_update_loop: lda nmt_update, X sta $2006 inx lda nmt_update, X sta $2006 inx lda nmt_update, X sta $2007 cpx nmt_update_len bcc @nmt_update_loop lda #0 sta nmt_update_len @scroll: lda scroll_nmt and #%00000011 ora #%10001000 sta $2000 lda scroll_x sta $2005 lda scroll_y sta $2005 lda #%00011110 sta $2001 ldx #0 stx nmi_ready @ppu_update_end: lda #0 sta nmi_lock @nmi_end: pla tay pla tax pla rti .segment "CODE" irq: rti .segment "CODE" ppu_update: lda #1 sta nmi_ready : lda nmi_ready bne :- rts ppu_skip: lda nmi_count : cmp nmi_count beq :- rts ppu_off: lda #2 sta nmi_ready : lda nmi_ready bne :- rts ppu_address_tile: lda $2002 tya lsr lsr lsr ora #$20 sta $2006 tya asl asl asl asl asl sta temp txa ora temp sta $2006 rts ppu_update_tile: pha txa pha ldx nmt_update_len tya lsr lsr lsr ora #$20 sta nmt_update, X inx tya asl asl asl asl asl sta temp pla ora temp sta nmt_update, X inx pla sta nmt_update, X inx stx nmt_update_len rts ppu_update_byte: pha tya pha ldy nmt_update_len txa sta nmt_update, Y iny pla sta nmt_update, Y iny pla sta nmt_update, Y iny sty nmt_update_len rts PAD_A = $01 PAD_B = $02 PAD_SELECT = $04 PAD_START = $08 PAD_U = $10 PAD_D = $20 PAD_L = $40 PAD_R = $80 .segment "ZEROPAGE" gamepad: .res 1 .segment "CODE" ; gamepad_poll: this reads the gamepad state into the variable labelled "gamepad" ; This only reads the first gamepad, and also if DPCM samples are played they can ; conflict with gamepad reading, which may give incorrect results. gamepad_poll: ; strobe the gamepad to latch current button state lda #1 sta $4016 lda #0 sta $4016 ; read 8 bytes from the interface at $4016 ldx #8 : pha lda $4016 ; combine low two bits and store in carry bit and #%00000011 cmp #%00000001 pla ; rotate carry into gamepad variable ror dex bne :- sta gamepad rts .segment "RODATA" example_palette: .byte $0F,$15,$26,$37 ; bg0 purple/pink .byte $0F,$09,$19,$29 ; bg1 green .byte $0F,$01,$11,$21 ; bg2 blue .byte $0F,$00,$10,$30 ; bg3 greyscale .byte $0F,$18,$28,$38 ; sp0 yellow .byte $0F,$14,$24,$34 ; sp1 purple .byte $0F,$1B,$2B,$3B ; sp2 teal .byte $0F,$12,$22,$32 ; sp3 marine .segment "ZEROPAGE" velocities: .res 8 cursor_x: .res 1 cursor_y: .res 1 temp_x: .res 1 temp_y: .res 1 var_m: .res 1 var_n: .res 1 var_o: .res 1 var_p: .res 1 frame_counter: .res 1 .segment "CODE" main: ldx #0 : lda example_palette, X sta palette, X inx cpx #32 bcc :- jsr setup_background jsr ppu_update lda #$40 sta cursor_x sta cursor_y lda #0 sta frame_counter jsr init_objects @loop: @draw: jsr draw jsr ppu_update lda frame_counter clc adc #1 sta frame_counter jmp @loop init_objects: lda #0 sta var_n ldx #0 : lda var_n clc adc #8 sta var_n sta oam+0, X ; Set Y position sta oam+3, X ; Set X position txa lsr lsr sta oam+1, X tay lda #0 sta velocities, Y inx inx inx inx cpx #(8*4) bne :- rts draw: lda frame_counter and #1 cmp #0 bne @end ldx #0 ldy #0 : lda velocities, Y clc adc #1 ; Add 2 to velocity sta velocities, Y lda oam+0, X clc adc velocities, Y sta oam+0, X cmp #200 bmi :+ lda #245 sta velocities, Y : inx inx inx inx iny cpy #8 bne :-- @end: rts setup_background: lda $2002 ; reset latch lda #$20 sta $2006 lda #$00 sta $2006 ; empty nametable lda #0 ldy #30 ; 30 rows : ldx #32 ; 32 columns : sta $2007 dex bne :- dey bne :-- ; set all attributes to 0 ldx #64 ; 64 bytes : sta $2007 dex bne :- ; fill in an area in the middle with 1/2 checkerboard lda #1 ldy #8 ; start at row 8 : pha ; temporarily store A, it will be clobbered by ppu_address_tile routine ldx #8 ; start at column 8 jsr ppu_address_tile pla ; recover A ldx #8 : sta $2007 eor #$3 inx cpx #(32-8) bcc :- eor #$3 iny cpy #(30-8) bcc :-- lda #$24 sta $2006 lda #$00 sta $2006 lda #$00 ldy #30 : ldx #32 : sta $2007 clc adc #1 and #3 dex bne :- clc adc #1 and #3 dey bne :-- lda #0 ldy #4 : ldx #16 : sta $2007 dex bne :- clc adc #%01010101 dey bne :-- rts