From 4489375854d674c88d4c2c2558bb1a6b72d5bb56 Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Thu, 13 Sep 2018 12:30:43 -0400 Subject: [PATCH] NES Hello World ROM This is a commit showing all the basic boilerplate needed to compile and link an rom for NES emulators. This requires cc65 to compile. The ROM will produce a buzzing square wave on the audio synthesizer and then loop forever, but it's a great "first ROM" and contains the documentation you really need to understand what's going on. --- .gitignore | 2 ++ Makefile | 27 ++++++++++++++++ README.md | 24 +++++++++++++-- src/helloworld.asm | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 src/helloworld.asm diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cd42ee3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bin/ +obj/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cba867c --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +SRCDIR = src +OBJDIR = obj +BINDIR = bin +TARGET = helloworld.nes + +SOURCES := $(wildcard $(SRCDIR)/*.asm) +OBJECTS := $(SOURCES:$(SRCDIR)/%.asm=$(OBJDIR)/%.o) +rm = rm -f + +.PHONY: all +all: directories $(BINDIR)/$(TARGET) + +.PHONY: directories +directories: + @mkdir -p $(OBJDIR) + @mkdir -p $(BINDIR) + +$(BINDIR)/$(TARGET): $(OBJECTS) + cl65 -t nes -o $@ $(OBJECTS) + +$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.asm + ca65 -o $@ $< + +.PHONY: clean +clean: + @$(rm) $(BINDIR)/$(TARGET) + @$(rm) $(OBJECTS) diff --git a/README.md b/README.md index bdf3ea8..b682bb3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,22 @@ -# nes_coding -Writing code for the NES +# NES coding exploration + +This is a project covering my exploration of coding 6502 assembly for the NES. + +The goal isn't to produce a game, per se, but to hopefully learn to work with +the NES architecture, tools for producing ROMS, etc. These little "lab +sessions" should be accessible to anyone with some modest background in systems +programming. + +These are developed on KDE Neon (Linux), and the Makefile may not work for +Windows. + +You will need a copy of [cc65](https://github.com/cc65/cc65) built and +installed. My preferred emulator/debugger is +[mednafen](https://mednafen.github.io/releases/), which on Ubuntu systems can be +installed with a simple `sudo apt-get install mednafen`. + +To build and run: +``` +make +mednafen bin/.nes +``` diff --git a/src/helloworld.asm b/src/helloworld.asm new file mode 100644 index 0000000..3ddbea6 --- /dev/null +++ b/src/helloworld.asm @@ -0,0 +1,77 @@ +; Hello World for the NES +; +; This initial program is purely to test out the assembling and linking +; features of cc65 and to prove out a build process that will make an +; iNES-format ROM suitable for use on most emulators. There is no console +; output for the NES and no default set of characters for text output, so +; the traditional "Hello World" output has been replaced with generating a +; simple square wave on the audio synthesizer. If your copy of mednafen is +; configured correctly for audio and everything builds correctly, you should +; hear a buzzing noise from the speakers and see a blank window/screen. +; +; The code to generate the square wave came from some code recommended by the +; NESDev Wiki at: +; https://wiki.nesdev.com/w/index.php/Programming_Basics#.22Hello.2C_world.21.22_program +; +; What was seriously lacking from this wiki, however, was information on how +; cc65 links together a complete ROM using the "-t nes" flag. Hopefully, this +; code will help spare a future hobbyist some time in understanding how everything +; goes together. + +; for reference on the linker config, I suggest you pull down the cc65 source and +; locate the "cfg/nes.cfg" file, which provides the layout of the ROM + + +; Mandatory iNES header. Without this, the emulator will likely not load your ROM. +; cc65 declares its location and size in the nes.cfg file +.segment "HEADER" + +.byte "NES", $1A ; "NES" magic value +.byte 2 ; number of 16KB code pages (we don't need 2, but nes.cfg declares 2) +.byte 1 ; number of 8KB "char" data pages +.byte $00 ; "mapper" and bank-switching type (0 for "none") +.byte $00 ; background mirroring flats + ; + ; Note the header is 16 bytes but the nes.cfg will zero-pad for us. + +; Like with "HEADER", "STARTUP" is declared in the nes.cfg file. Your code must +; fit within this 32 KB segment (for now). All your code effectively goes here. +; I will not go into length on 6502 assembly, but I will note this loads a buzzy +; square wave into the audio synthesizer and then loops forever. You must supply +; the label for your startup code in the "VECTORS" segment below + +.segment "STARTUP" + +reset: + lda #$01 ; square 1 + sta $4015 + lda #$08 ; period low + sta $4002 + lda #$02 ; period high + sta $4003 + lda #$bf ; volume + sta $4000 +forever: + jmp reset + +nmi: + rti ; Return from the NMI (NTSC refresh interrupt) + +; "VECTORS" is another mandatory section. It must contain a table for three +; things, in order. The first is for the non-maskable interrupt (NMI), which +; is the NTSC video signal vertical sync (among other things). This interrupt +; triggers on the end of every pass of an NTSC television and is crucial for +; timing game logic. The second is the entry point of your program, triggered +; off the "reset" interrupt which is delivered at power on and at any press of +; the NES reset button. The third handles assorted interrupt requests (IRQs) +; from the system and is not used in this program. + +.segment "VECTORS" +.word nmi +.word reset +.word 0 + +; "CHARS" is a reserved section for data. Our program has no data, so this +; is only here to help the linker finish laying out the ROM. + +.segment "CHARS" -- 2.34.1