diff --git a/assemble.sh b/assemble.sh new file mode 100644 index 0000000..4f3df48 --- /dev/null +++ b/assemble.sh @@ -0,0 +1,5 @@ +#! /bin/sh + +ca65 src/helloworld.asm +ca65 src/reset.asm +ld65 src/reset.o src/helloworld.o -C nes.cfg -o helloworld.nes diff --git a/helloworld.nes b/helloworld.nes new file mode 100644 index 0000000..6e3e5ce Binary files /dev/null and b/helloworld.nes differ diff --git a/nes.cfg b/nes.cfg new file mode 100644 index 0000000..12f8cf4 --- /dev/null +++ b/nes.cfg @@ -0,0 +1,22 @@ +MEMORY { + HEADER: start=$00, size=$10, fill=yes, fillval=$00; + ZEROPAGE: start=$10, size=$ff; + STACK: start=$0100, size=$0100; + OAMBUFFER: start=$0200, size=$0100; + RAM: start=$0300, size=$0500; + ROM: start=$8000, size=$8000, fill=yes, fillval=$ff; + CHRROM: start=$0000, size=$2000; +} + +SEGMENTS { + HEADER: load=HEADER, type=ro, align=$10; + ZEROPAGE: load=ZEROPAGE, type=zp; + STACK: load=STACK, type=bss, optional=yes; + OAM: load=OAMBUFFER, type=bss, optional=yes; + BSS: load=RAM, type=bss, optional=yes; + DMC: load=ROM, type=ro, align=64, optional=yes; + CODE: load=ROM, type=ro, align=$0100; + RODATA: load=ROM, type=ro, align=$0100; + VECTORS: load=ROM, type=ro, start=$FFFA; + CHR: load=CHRROM, type=ro, align=16, optional=yes; +} \ No newline at end of file diff --git a/src/background.nam b/src/background.nam new file mode 100644 index 0000000..bbfc52f Binary files /dev/null and b/src/background.nam differ diff --git a/src/constants.inc b/src/constants.inc new file mode 100644 index 0000000..5acaec0 --- /dev/null +++ b/src/constants.inc @@ -0,0 +1,7 @@ +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +PPUADDR = $2006 +PPUDATA = $2007 +OAMADDR = $2003 +OAMDMA = $4014 \ No newline at end of file diff --git a/src/header.inc b/src/header.inc new file mode 100644 index 0000000..fe09113 --- /dev/null +++ b/src/header.inc @@ -0,0 +1,8 @@ +.segment "HEADER" +.byte $4e, $45, $53, $1a ; Magic string that always begins an iNES header +.byte $02 ; Number of 16KB PRG-ROM banks +.byte $01 ; Number of 8KB CHR-ROM banks +.byte %00000001 ; Vertical mirroring, no save RAM, no mapper +.byte %00000000 ; No special-case flags set, no mapper +.byte $00 ; No PRG-RAM present +.byte $00 ; NTSC format \ No newline at end of file diff --git a/src/helloworld.asm b/src/helloworld.asm new file mode 100644 index 0000000..b5ffd6a --- /dev/null +++ b/src/helloworld.asm @@ -0,0 +1,421 @@ +.include "constants.inc" +.include "header.inc" + +.segment "CODE" +.proc irq_handler + RTI +.endproc + +.proc nmi_handler + LDA #$00 + STA OAMADDR + LDA #$02 + STA OAMDMA + + ;Update tiles *after* DMA transfer + JSR update_player + JSR draw_player + + LDA #$00 + STA $2005 + STA $2005 + RTI +.endproc + +.import reset_handler + + +.proc draw_player + ;save registers + PHP + PHA + TXA + PHA + TYA + PHA + + ;write player ship tile numbers + + LDA #$05 + STA $0201 + LDA #$06 + STA $0205 + LDA #$07 + STA $0209 + LDA #$08 + STA $020D + + ; write player ship tile attributes + ; use palette 0 + LDA #$00 + STA $0202 + STA $0206 + STA $020A + STA $020E + + ; store tile locations + ; top left tile: + LDA player_y + STA $0200 + LDA player_x + STA $0203 + + ;top right tile (x+8): + LDA player_y + STA $0204 + LDA player_x + CLC + ADC #$08 + STA $0207 + + ;bottom left tile (y+8) + LDA player_y + CLC + ADC #$08 + STA $0208 + LDA player_x + STA $020B + + ;bottom right tile (x+8, y+8) + LDA player_y + CLC + ADC #$08 + STA $020C + LDA player_x + CLC + ADC #$08 + STA $020F + + ; restore registers and return + PLA + TAY + PLA + TAX + PLA + PLP + RTS +.endproc + +.proc update_player + PHP + PHA + TXA + PHA + TYA + PHA + + LDA player_x + CMP #$e0 + BCC not_at_right_edge + ; if BCC is not taken, we are greater than $e0 + LDA #$00 + STA player_dir ; start moving left + JMP direction_set ; we already chose a direction, + ; so we can skip the left side check +not_at_right_edge: + LDA player_x + CMP #$10 + BCS direction_set + ; if BCS not taken, we are less than $10 + LDA #$01 + STA player_dir ; start moving right +direction_set: + ; now, actually update player_x + LDA player_dir + CMP #$01 + BEQ move_right + ; if player_dir minus $01 is not zero, + ; that means player_dir was $00 and + ; we need to move left + DEC player_x + JMP exit_subroutine +move_right: + INC player_x +exit_subroutine: + ; all done, clean up an return + PLA + TAY + PLA + TAX + PLA + PLP + RTS +.endproc + +.export main +.proc main + + LDX PPUSTATUS + LDX #$3f + STX PPUADDR + LDX #$00 + STX PPUADDR + + LDX #$00 +load_palettes: + LDA palettes,X + STA PPUDATA + INX + CPX #$20 + BNE load_palettes + + LDX #$00 + + ; write a nametable + ; Big Stars + + LDA PPUSTATUS + LDA #$21 + STA PPUADDR + LDA #$72 + STA PPUADDR + LDX #$2F + STX PPUDATA + + LDA PPUSTATUS + LDA #$20 + STA PPUADDR + LDA #$68 + STA PPUADDR + LDX #$2F + STX PPUDATA + + LDA PPUSTATUS + LDA #$20 + STA PPUADDR + LDA #$74 + STA PPUADDR + LDX #$2F + STX PPUDATA + + LDA PPUSTATUS + LDA #$21 + STA PPUADDR + LDA #$5A + STA PPUADDR + LDX #$2F + STX PPUDATA + + LDA PPUSTATUS + LDA #$21 + STA PPUADDR + LDA #$CB + STA PPUADDR + LDX #$2F + STX PPUDATA + + LDA PPUSTATUS + LDA #$21 + STA PPUADDR + LDA #$64 + STA PPUADDR + LDX #$2F + STX PPUDATA + + LDA PPUSTATUS + LDA #$22 + STA PPUADDR + LDA #$C7 + STA PPUADDR + LDX #$2F + STX PPUDATA + + LDA PPUSTATUS + LDA #$22 + STA PPUADDR + LDA #$55 + STA PPUADDR + LDX #$2F + STX PPUDATA + + LDA PPUSTATUS + LDA #$22 + STA PPUADDR + LDA #$B9 + STA PPUADDR + LDX #$2F + STX PPUDATA + + LDA PPUSTATUS + LDA #$23 + STA PPUADDR + LDA #$6D + STA PPUADDR + LDX #$2F + STX PPUDATA + + ; write a nametable + ; Medium Stars + + LDA PPUSTATUS + LDA #$20 + STA PPUADDR + LDA #$64 + STA PPUADDR + LDX #$2D + STX PPUDATA + + LDA PPUSTATUS + LDA #$20 + STA PPUADDR + LDA #$4F + STA PPUADDR + LDX #$2D + STX PPUDATA + + LDA PPUSTATUS + LDA #$20 + STA PPUADDR + LDA #$B9 + STA PPUADDR + LDX #$2D + STX PPUDATA + + LDA PPUSTATUS + LDA #$20 + STA PPUADDR + LDA #$DD + STA PPUADDR + LDX #$2D + STX PPUDATA + + LDA PPUSTATUS + LDA #$21 + STA PPUADDR + LDA #$4D + STA PPUADDR + LDX #$2D + STX PPUDATA + + LDA PPUSTATUS + LDA #$20 + STA PPUADDR + LDA #$D1 + STA PPUADDR + LDX #$2D + STX PPUDATA + + ; write a nametable + ; Small Stars + LDA PPUSTATUS + LDA #$20 + STA PPUADDR + LDA #$3A + STA PPUADDR + LDX #$2E + STX PPUDATA + + LDA PPUSTATUS + LDA #$20 + STA PPUADDR + LDA #$B9 + STA PPUADDR + LDX #$2E + STX PPUDATA + + LDA PPUSTATUS + LDA #$21 + STA PPUADDR + LDA #$4D + STA PPUADDR + LDX #$2E + STX PPUDATA + + LDA PPUSTATUS + LDA #$21 + STA PPUADDR + LDA #$4D + STA PPUADDR + LDX #$2E + STX PPUDATA + + LDA PPUSTATUS + LDA #$21 + STA PPUADDR + LDA #$F0 + STA PPUADDR + LDX #$2E + STX PPUDATA + + LDA PPUSTATUS + LDA #$22 + STA PPUADDR + LDA #$26 + STA PPUADDR + LDX #$2E + STX PPUDATA + + LDA PPUSTATUS + LDA #$22 + STA PPUADDR + LDA #$E4 + STA PPUADDR + LDX #$2E + STX PPUDATA + + LDA PPUSTATUS + LDA #$22 + STA PPUADDR + LDA #$CE + STA PPUADDR + LDX #$2E + STX PPUDATA + + LDA PPUSTATUS + LDA #$23 + STA PPUADDR + LDA #$36 + STA PPUADDR + LDX #$2E + STX PPUDATA + + ; finally, attribute table + LDA PPUSTATUS + LDA #$23 + STA PPUADDR + LDA #$D4 + STA PPUADDR + LDA #%01110000 + STA PPUDATA + +vblankwait: ; wait for another vblank before continuing + BIT PPUSTATUS + BPL vblankwait + + LDA #%10010000 ; turn on NMIs, sprites use first pattern table + STA PPUCTRL + LDA #%00011110 ; turn on screen + STA PPUMASK +forever: + JMP forever +.endproc + +.segment "ZEROPAGE" +player_x: .res 1 +player_y: .res 1 +player_dir: .res 1 +.exportzp player_x, player_y + +.segment "RODATA" +palettes: + .byte $0f, $12, $23, $27 + .byte $0f, $2b, $3c, $39 + .byte $0f, $0c, $07, $13 + .byte $0f, $19, $09, $29 + + .byte $0f, $2d, $10, $15 + .byte $0f, $19, $09, $29 + .byte $0f, $19, $09, $29 + .byte $0f, $19, $09, $29 + +sprite: + .byte $70, $05, $00, $80 + .byte $70, $06, $00, $88 + .byte $78, $07, $00, $80 + .byte $78, $08, $00, $88 + +.segment "VECTORS" +.addr nmi_handler, reset_handler, irq_handler + +.segment "CHR" +.incbin "starfield.chr" \ No newline at end of file diff --git a/src/helloworld.o b/src/helloworld.o new file mode 100644 index 0000000..c0e2b20 Binary files /dev/null and b/src/helloworld.o differ diff --git a/src/reset.asm b/src/reset.asm new file mode 100644 index 0000000..cd67c3c --- /dev/null +++ b/src/reset.asm @@ -0,0 +1,43 @@ +.include "constants.inc" + +.segment "ZEROPAGE" +.importzp player_x, player_y + +.segment "CODE" +.import main +.export reset_handler +.proc reset_handler + SEI + CLD + LDX #$40 + STX $4017 + LDX #$FF + TXS + INX + STX PPUCTRL + STX PPUMASK + STX $4010 + BIT PPUSTATUS +vblankwait: + BIT PPUSTATUS + BPL vblankwait + + LDX #$00 + LDA #$FF +clear_oam: + STA $0200, X + INX + INX + INX + INX + BNE clear_oam +vblankwait2: + BIT PPUSTATUS + BPL vblankwait2 + ; initialize zero-page values + LDA #$80 + STA player_x + LDA #$a0 + STA player_y + JMP main +.endproc \ No newline at end of file diff --git a/src/reset.o b/src/reset.o new file mode 100644 index 0000000..0e5078a Binary files /dev/null and b/src/reset.o differ diff --git a/src/starfield.chr b/src/starfield.chr new file mode 100644 index 0000000..6562607 Binary files /dev/null and b/src/starfield.chr differ