commit d6851d6f406f985eaec740624ddc9469f96400ea Author: RochesterX Date: Sat Feb 14 18:08:10 2026 -0500 Initial diff --git a/-game.o b/-game.o new file mode 100644 index 0000000..1bc57f7 Binary files /dev/null and b/-game.o differ diff --git a/background.chr b/background.chr new file mode 100644 index 0000000..b585044 Binary files /dev/null and b/background.chr differ diff --git a/game.cfg b/game.cfg new file mode 100644 index 0000000..8342b4d --- /dev/null +++ b/game.cfg @@ -0,0 +1,19 @@ +MEMORY { + ZP: start = $00, size = $0100, type = rw, file = ""; + OAM: start = $0200, size = $0100, type = rw, file = ""; + RAM: start = $0300, size = $0500, type = rw, file = ""; + HDR: start = $0000, size = $0010, type = ro, file = %O, fill = yes, fillval = $00; + PRG: start = $8000, size = $8000, type = ro, file = %O, fill = yes, fillval = $00; + CHR: start = $0000, size = $2000, type = ro, file = %O, fill = yes, fillval = $00; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + OAM: load = OAM, type = bss, align = $100; + BSS: load = RAM, type = bss; + HEADER: load = HDR, type = ro; + CODE: load = PRG, type = ro, start = $8000; + RODATA: load = PRG, type = ro; + VECTORS: load = PRG, type = ro, start = $FFFA; + TILES: load = CHR, type = ro; +} diff --git a/game.nes b/game.nes new file mode 100644 index 0000000..eb36e6c Binary files /dev/null and b/game.nes differ diff --git a/game.o b/game.o new file mode 100644 index 0000000..40e7c17 Binary files /dev/null and b/game.o differ diff --git a/game.s b/game.s new file mode 100644 index 0000000..b96ad37 --- /dev/null +++ b/game.s @@ -0,0 +1,530 @@ +.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 + diff --git a/run b/run new file mode 100755 index 0000000..762473a --- /dev/null +++ b/run @@ -0,0 +1,5 @@ +#!/bin/bash +ca65 game.s -o game.o +ld65 game.o -C game.cfg -o game.nes +fceux game.nes + diff --git a/sprite.chr b/sprite.chr new file mode 100644 index 0000000..6847b19 Binary files /dev/null and b/sprite.chr differ diff --git a/temp.png b/temp.png new file mode 100644 index 0000000..692ff22 Binary files /dev/null and b/temp.png differ