Pattern, name, and attribute tables lab_3
authorRhett Aultman <roadriverrail@gmail.com>
Tue, 18 Sep 2018 04:59:39 +0000 (00:59 -0400)
committerRhett Aultman <roadriverrail@gmail.com>
Tue, 18 Sep 2018 04:59:39 +0000 (00:59 -0400)
This commit shows how to load pattern, name, and attribute tables, and
explains their roles in rendering graphics.  A simple pattern table with
text characters as well as some multi-pattern graphics is shown.

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

index aa81aa7463fc3b7dd91e921f40a9549fde3fbfa9..a8a75eaf0871892405553ffd918cf612d06a2857 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 SRCDIR = src
 OBJDIR = obj
 BINDIR = bin
-TARGET = bgpalette.nes
+TARGET = background.nes
 
 SOURCES := $(wildcard $(SRCDIR)/*.asm)
 OBJECTS := $(SOURCES:$(SRCDIR)/%.asm=$(OBJDIR)/%.o)
@@ -23,5 +23,5 @@ $(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.asm
 
 .PHONY: clean
 clean:
-       @$(rm) $(BINDIR)/$(TARGET)
+       @$(rm) $(BINDIR)/*
        @$(rm) $(OBJECTS)
index 3ac5083f834109a1431062cec2ca158141063e7d..f8332f300eb317c10c21bb3a4684afaedc28d1d5 100644 (file)
--- a/README.md
+++ b/README.md
@@ -11,15 +11,19 @@ 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`.
+installed.  My preferred emulator/debugger is [fceux](http://www.fceux.com) under 
+Wine, and **NOT** the one under Linux, as their Windows release has a powerful
+set of debugging tools available.  With them, you can inspect pattern and name tables!
+
+Alternatively, I'd recommend [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
+* Lab 1: Hello World!
+* Lab 2: Drawing a background color
+* Lab 3: Pattern, name, and attribute tables
 
 To build and run:
 ```
diff --git a/src/background.asm b/src/background.asm
new file mode 100644 (file)
index 0000000..bf6afc3
--- /dev/null
@@ -0,0 +1,178 @@
+; Nametables and attribute tables Demo for NES
+; Following on from the background palette program, this program looks at how
+; to load a pattern table, which contains the actual graphic tiles, a nametable,
+; which contains the layout of graphic tiles for the background, and the
+; attribute table, which specifies what palette to paint everything with.
+;
+; I am not an artist, and even then, converting bitmaps to pattern tables can be
+; a chore.  So, I used the "Generitiles" from the NESDev wiki
+; (https://wiki.nesdev.com/w/index.php/Placeholder_graphics).  I then ran these
+; through a Python script supplied by Damian Yerrick 
+; (https://github.com/pinobatch/nesbgeditor) which converts a 2-bit PNG into
+; pattern tables pretty darn effectively.  Thanks, Damian!
+; 
+; So, we're now *finally* using the CHARS section, which gets directly mapped
+; into the PPU's memory at power on.  This section really should more
+; be named "CHR-ROM", as this is the more common name for it.  You'll notice
+; that I can directly include the file produced by Damian's tools, which keeps
+; the code tidy.
+;
+; With the patterns directly mapped in, the next step is to load up some data
+; in the name table.  Since we're not doing anything fancy, I've restricted
+; things to the first of the two name tables.  Much like we did for loading the
+; palette colors, we load this through the use of PPUADDR and PPUDATA.
+;
+; Finally, we load the attribute table, which says which palette to use for
+; each 32x32 pixel region on the screen.  In a more advanced demo, this would
+; raise the number of effective colors I was using on the screen.  For now,
+; though, I just want to keep things simple and easy-to-explain.
+;
+; Note that the annyoing "hello world" sound is now gone.  The graphics show
+; that everything is working.
+
+.define PPUMASK      $2001
+.define PPUSTATUS    $2002
+.define PPUADDR      $2006
+.define PPUSCROLL    $2005
+.define PPUDATA      $2007
+
+.define NAMETABLE_0_HI $20
+.define NAMETABLE_0_LO $00
+.define ATTRTABLE_0_HI $23
+.define ATTRTABLE_0_LO $C0
+.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
+
+  ; Interesting little fact I learned along the way.  Because it takes two
+  ; stores on PPUADDR to move its pointer, it's good practice to start all of
+  ; your PPUADDR use with a peek at PPUSTATUS since this resets its "latch"
+  ; and ensures you're addressing the address you expect.
+  ; Technically, we don't need this because we did it in the reset code, but
+  ; it's a neat little thing to mention here
+
+  bit PPUSTATUS
+
+  ; load the background palette
+  lda #BGPALETTE_HI
+  sta PPUADDR
+  lda #BGPALETTE_LO
+  sta PPUADDR
+
+  ; prep the loop
+  ldx #0
+
+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)
+
+; move PPUADDR over to nametable 0. 
+  lda #NAMETABLE_0_HI
+  sta PPUADDR
+  lda #NAMETABLE_0_LO
+  sta PPUADDR
+
+; This loop iterates over the pattern table, outputting it in lines of 16
+; The other 16 are just padded out with a pattern that's blank.  This lets
+; me easily show you some simple graphics that are made up of multiple
+; stacked tiles without getting too fancy.  In reality, you'd probably have
+; complete nametables that you'd load in from files and simply run in a loop.
+
+  ldx #$00
+  ldy #16
+nametableloop:
+  stx PPUDATA
+  inx
+  dey
+  bne nametableloop
+  ldy #16
+padright:
+  sta PPUDATA
+  dey
+  bne padright
+  cpx $D0
+  beq done
+  ldy #16
+  jmp nametableloop
+done:
+  
+; set up Palette 0 for everything
+  bit PPUSTATUS
+  lda #ATTRTABLE_0_HI
+  sta PPUADDR
+  lda #ATTRTABLE_0_LO
+  sta PPUADDR
+  ldx #64 ; 64 tiles in the attribute table
+  lda #0
+
+attrloop:
+  sta PPUDATA
+  dex
+  bne attrloop
+
+; Enable background and sprite rendering.  This is suuuuuper important to
+; remember.  I didn't remember to put this in and probably blew a whole day
+; trying to figure out why my emulator hated me.
+  lda #$1e
+  sta PPUMASK
+
+forever:
+  jmp forever
+
+nmi:
+  rti ; Return from the NMI (NTSC refresh interrupt)
+
+
+; The background colors are, in order:
+; $0F: black
+; $15: red
+; $22: blue
+; $20: white
+
+bgpalette:
+  .byte $0F, $15, $22, $20 ; palette 0
+  .byte $0F, $15, $22, $20 ; palette 1
+  .byte $0F, $15, $22, $20 ; palette 2
+  .byte $0F, $15, $22, $20 ; palette 3
+
+
+; vectors declaration
+.segment "VECTORS"
+.word nmi
+.word reset
+.word 0
+
+; As mentioned above, this is the place where you put your pattern table data
+; so that it can automatically be mapped into the PPU's memory at $0000-$1FFF.
+; Note the use of .incbin so I can just import a binary file.  Neato!
+
+.segment "CHARS"
+.incbin "generitiles.pat"
diff --git a/src/bgpalette.asm b/src/bgpalette.asm
deleted file mode 100644 (file)
index aae3597..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-; 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/generitiles.pat b/src/generitiles.pat
new file mode 100644 (file)
index 0000000..5b904dc
Binary files /dev/null and b/src/generitiles.pat differ