Draw a background color lab_2
authorRhett Aultman <roadriverrail@gmail.com>
Sat, 15 Sep 2018 15:20:08 +0000 (11:20 -0400)
committerRhett Aultman <roadriverrail@gmail.com>
Sat, 15 Sep 2018 15:23:05 +0000 (11:23 -0400)
This demo builds on the previous one and focuses on waiting for the NES
PPU to initialize, interacting with VRAM addresses, loading the
background palettes, and getting a background color to draw on the
screen.

Makefile
README.md
src/bgpalette.asm [new file with mode: 0644]
src/helloworld.asm [deleted file]

index cba867c03877d8ff02415b162252c517e662524a..aa81aa7463fc3b7dd91e921f40a9549fde3fbfa9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 SRCDIR = src
 OBJDIR = obj
 BINDIR = bin
-TARGET = helloworld.nes
+TARGET = bgpalette.nes
 
 SOURCES := $(wildcard $(SRCDIR)/*.asm)
 OBJECTS := $(SOURCES:$(SRCDIR)/%.asm=$(OBJDIR)/%.o)
index b682bb30fe3ad3e30d7de815eded1f83ea7d2d7e..3ac5083f834109a1431062cec2ca158141063e7d 100644 (file)
--- a/README.md
+++ b/README.md
@@ -15,6 +15,12 @@ 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`.
 
+My latest example code will always live on the master branch.  To see earlier
+demos/labs, please look at the labXX branches.  Current ones include:
+
+Lab 1: Hello World!
+Lab 2: Drawing a background color
+
 To build and run:
 ```
 make
diff --git a/src/bgpalette.asm b/src/bgpalette.asm
new file mode 100644 (file)
index 0000000..aae3597
--- /dev/null
@@ -0,0 +1,140 @@
+; Background Palette Loader Demo For NES
+;
+; Following on from the hello world program, this program expands out into
+; interactions with the NES PPU (Picture Processing Unit).  This does the bare
+; minimum to draw the screen-- it disables all sprites and background graphics
+; and fills the screen with the default color.
+;
+; Doing this requires some extra work.  Firsst off, the PPU is not immediately
+; ready at power on.  Before interacting with it, you need to wait enough cycles
+; for it to become ready.  There is a standard wait process employed in the
+; reset vector.
+;
+; Loading information into the PPU involves poking specific memory addresses.
+; On the NEW, the PPU and CPU are basically independent of each other and they
+; each have their own RAM space (the PPU's is called VRAM).  Loading data into
+; VRAM is done by setting the address by writing it, one byte at a time, to the
+; PPUADDR ($2006) register.  After that, VRAM can be read and written to using
+; lda or sta instructions from the PPUDATA ($2007) register.
+;
+; The PPU stores 4 different background palettes; each stores 3 colors.  A 4th
+; color, the default background color, is stored at VRAM $3F00 and aliased at
+; $3F04, $3F08, and $3F0C.  (They're also aliased at $3F10, $3F14, $3F18, and
+; $3F1C).  Because any write to those aliases can change the background color,
+; it's easier to treat the palette as containing FOUR colors, with the first
+; one always being the same.
+; 
+; In this demo, all the palettes are the same.  It's actually one of the
+; background palettes from Super Mario Brothers
+
+
+.define PPUMASK      $2001
+.define PPUSTATUS    $2002
+.define PPUADDR      $2006
+.define PPUDATA      $2007
+
+.define BGPALETTE_HI $3F
+.define BGPALETTE_LO $00
+
+; Mandatory iNES header.
+.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.
+
+; code ROM segment
+; all code and on-ROM program data goes here
+
+.segment "STARTUP"
+
+; reset vector
+reset:
+  bit PPUSTATUS  ; clear the VBL flag if it was set at reset time
+vwait1:
+  bit PPUSTATUS
+  bpl vwait1     ; at this point, about 27384 cycles have passed
+vwait2:
+  bit PPUSTATUS
+  bpl vwait2     ; at this point, about 57165 cycles have passed
+
+  lda #$00      ; color image, no sprites, no background
+  sta PPUMASK
+
+  ; request access to the background palette memory.  This starts at VRAM
+  ; address $3F00.  VRAM is in a separate address space from RAM, so we access
+  ; it via the use of PPUADDR and PPUDATA.  PPUADDR sets the address to access
+  ; while PPUDATA is the data, one byte at a time.  When you access VRAM via
+  ; PPUDATA, it auto-increments the address by default, making loops easier.
+
+  ; Start at VRAM address $3F00
+  lda #BGPALETTE_HI
+  sta PPUADDR
+  lda #BGPALETTE_LO
+  sta PPUADDR
+
+  ; prep the loop
+  ldx #0
+
+  ; load the background palette
+paletteloop:
+  lda bgpalette, X ; load from the bgpalette array
+  sta PPUDATA      ; store in PPUDATA, PPU will auto-increment
+  inx              ; increment the X (index) register
+  cpx #16
+  bne paletteloop  ; run the loop until X=16 (size of the palettes)
+
+
+  ; The "hello world" sound from the previous exercise, so we know we made
+  ; it to the end of the reset vector.
+  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 forever
+
+nmi:
+  rti ; Return from the NMI (NTSC refresh interrupt)
+
+
+; The background colors are, in order:
+; $22: pale blue
+; $29: drab green
+; $1A: forest green
+; $0F: black
+; Actual color fidelity varies based on emulator and monitor, but
+; this gives a rough sense of the colors
+;
+; Also note that my practical experience shows me that you *must*
+; load up all 4 palettes in order to get the PPU to be happy and
+; draw your default color to the screen!
+
+bgpalette:
+  .byte $22, $29, $1A, $0F ; palette 0
+  .byte $22, $29, $1A, $0F ; palette 1
+  .byte $22, $29, $1A, $0F ; palette 2
+  .byte $22, $29, $1A, $0F ; palette 3
+
+
+; vectors declaration
+.segment "VECTORS"
+.word nmi
+.word reset
+.word 0
+
+; The "hello world" program identified this as a space for ROM data.  That is
+; technically true, but it misses a verey critical nuance.  This segment is for
+; CHR-ROM, which is a space in ROM which the PPU directly loads into its memory
+; for use in rendering the screen!  This is not the rodata section!  Your
+; read-only data used in your program belongs in the STARTUP segment!
+
+.segment "CHARS"
diff --git a/src/helloworld.asm b/src/helloworld.asm
deleted file mode 100644 (file)
index b1af0b2..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-; 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 forever
-
-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"