Skip to content

Latest commit

 

History

History
672 lines (442 loc) · 17 KB

CS-98-026.md

File metadata and controls

672 lines (442 loc) · 17 KB

...menustart

...menuend

CS 98-026

NES FQA

NES resolution

  • 248x240 visible
  • From the programmer's perspective, the NES has a resolution of 256x256,
    • but it chops off 8 pixels on each side, and 16 pixels on the bottom.

NES Color Palettes

  • 64 Colors in the system palette (not fully unique)
  • 16 Colors in the background palette
  • 16 Colors in the foreground palette

NES Sub Palettes

  • Background and Foreground palettes are split into four sub palettes of 4 colors.
  • The first entry in each background sub palette is the same primary background color.
    • This primary background color is usually used for the sky color
    • but it can also be used as the transparent color, in the rare event that you may wish to draw a sprite “behind” the background.
  • The first entry in each foreground sub palette is transparent.

  • Sprites and background tiles are 2-bit color (4 colors max).
  • Each pixel color value is a lookup index , into the current active sub palette for that tile
  • This allows the primary color and 3 unique colors per background sub palette.
  • Each foreground sub palette has 3 nontransparent colors.
  • The game is allowed to change the palettes and sub palettes during play,
    • and sprites and tiles may change which sub palette they use for drawing.
  • How many colours can the NES display at once?
    • Ordinarily, the NES can display 13 different background colours, and 12 different sprite colours, giving a total of 25 colours on-screen, at once
    • However, using PPU trickery, one can display all of the NES' 64 palette values at once.

NES Sprites

  • 8x8 or 8x16 pixels
    • However, only one size may be active at a time.
  • May be flipped horizontally or vertically.
  • 3 colors and transparency
    • However, you may cleverly draw multiple sprites on top of each other, to create the appearance of a single sprite with more colors
  • Maximum of 64 sprites on screen at once
  • Hardware limit of 8 sprites per scanline
  • Not all emulators respect the 8 sprite limit

Image Scaling

  • NES has a fixed resolution of 256x240
  • Your monitor is bigger
  • Playing games in a tiny window is no fun
  • hq3x seems good

How a Game Works

  • Initialization
    • Set up graphics
    • Set initial values
  • The Game Loop
    • Draw the screen
    • Get player input
    • Calculate the next frame

Example nbasic Game

start:
    gosub init_screen
    gosub init_vars
mainloop:
    gosub vwait //wait for next frame
    gosub draw_stuff //draw the new frame
    gosub handle_joystick //get input
    gosub calculate_things //game logic
    gosub update_sound //make some noise
    goto mainloop

NES Registers

Common Registers

address desc
$2000, $2001 PPU Initialization
$2003, $2004 Sprite Control
$2005 Scrolling
$2006 PPU Memory Address
$2007 PPU Memory Access
$4014 Sprite DMA
$4000-$400F Music Control
$4015 Sound Setup
$4016, $4017 Joysticks

Joystick Input

  • Joystick 1 is register $4016
  • Joystick 2 is register $4017
  • Strobe by writing 1 then 0 to port
    • 对$4016或$4017先写 1 后写 0,这将在游戏手柄的电路里发生个选通脉冲, 对手柄复位
    • 然后可以在$4016或$4017读取了
  • Read one byte for each button (A, B, Select, Start, Up, Down, Left, Right)
    • 每次读取将返回一个单一的按钮的状态
  • Bit 0 tells button status
    • 1 表示键被按下
  • Bit 1,2 用于扩展接口
// NES Joystick usage code, by Bob Rost
// Simply "gosub joystick1" to update info about player 1's controller
// nbasic automatically allocates variables joy1a, joy1b, etc.
// Similar usage for player 2.

joystick1:
    set $4016 1 //first strobe byte
    set $4016 0 //second strobe byte
    set joy1a       & [$4016] 1
    set joy1b       & [$4016] 1
    set joy1select  & [$4016] 1
    set joy1start   & [$4016] 1
    set joy1up      & [$4016] 1
    set joy1down    & [$4016] 1
    set joy1left    & [$4016] 1
    set joy1right   & [$4016] 1
    return

joystick2:
    set $4017 1 //first strobe byte
    set $4017 0 //second strobe byte
    set joy2a       & [$4017] 1
    set joy2b       & [$4017] 1
    set joy2select  & [$4017] 1
    set joy2start   & [$4017] 1
    set joy2up      & [$4017] 1
    set joy2down    & [$4017] 1
    set joy2left    & [$4017] 1
    set joy2right   & [$4017] 1
    return
  • 大塞车中的代码
LDY #$01   ; select joypad0
STY $4016
DEY 
STY $4016

LDA $4016  ; read 
AND #$03   ; 适配 扩展接口?
STA $6D    ; A键值 存入6D单元

LDA $4016  ; read 
AND #$03   ; 
STA $6E    ; B键值 存入6E单元

...

Accessing PPU Memory

  • PPU has its own memory space, which you can access through registers
  • Write high and low byte of target PPU address to PPUADDR ($2006)
  • Read and write PPUDATA($2007). Internal pointer auto-increments

Drawing Sprites

  • Each sprite has 4 attribute bytes
    • Y coordinate
    • Tile number
    • Attribute byte
    • X coordinate
  • Set the sprite memory, through either PPU memory access or DMA

Sprite Attribute Byte

  • bits 0-1 Sub palette
  • bits 2-4 unused
  • bit 5 behind background
  • bit 6 flip horizontal
  • bit 7 flip vertical

Setting the Palette

  • Background palette has PPU address $3F00
  • Foreground palette has PPU address $3F10
  • Write the global palette indices to PPU memory
set $2006 $3f
set $2006 $00
set $2007 [palette 0]
...
set $2007 [palette 31]

Scrolling

  • Wait for vertical retrace
  • Write horizontal scroll value to $2005
  • Write vertical scroll value to $2005

NES Music

Computer Music

  • MIDI
    • Stores note lengths, pitch and timing
    • Instrument channels, standard patches (General MIDI)
    • One instrument, multiple notes per channel at a time
  • MOD / S3M / various others
    • Stores note lengths, pitch and timing
    • Sound channels, custom samples
    • One sample, one note per channel at a time

NES Music Capabilities

  • Program note lengths, pitch, timing, other effects
  • One sound per channel at a time
  • 6 Octave range
  • 5 Music Channels
    • 0: Square wave
    • 1: Square wave 2
    • 2:Triangle wave
    • 3: Noise channe

nesmus

  • Music programming language derived from QBasic
  • Code written with a standard text editor
  • Translated into a nesmus data file
  • Data may be interpreted by game

From Score to Text

  • Two simultaneous parts
  • Repeat
  • 120 bpm, just for fun, (Beats per Minute)

  • Music notation translates directly to nesmus notation
  • Now put it all together to make nesmus code
    • 注:下面的这段代码有错
t120
loop 2
channel 0
o3 l8 cdedf2ed <b>cddd3p8 dc<b>dc<bg>d c<g>cec3p8
channel 1
;changed to o2 so it becomes a bass part
o2 l4 c<g>c<g >d<g>d<g bgbg >c<g>cp4
end

Listening

  • Compile to “music.dat”, then assemble to “music.nes”
nesmus test.mus music.dat
...
nesasm music.asm

Using nesmus Data

  • Include nesmus binary data in your game
  • Include nesmus interpreter in your game
  • Webpage Resources section
  • RTFM

Drawing Sprites

  • 3 colors, plus transparent
  • Multiples of 8x8 pixels, often of 8x16

Sprite Tiles in the Pattern Table

  • Tile pairs (8x16 sprites) must be even aligned
    • If top drawn tile is 2nᵗʰ from pattern table, matching bottom tile is (2n+1)ᵗʰ.
    • IN EASY ENGLISH:Tiles 0 and 1 can make a sprite pair.Tiles 1 and 2 can’t.

The Pattern Table

  • Remember, there are 256 tiles per table
  • Each tile is 8x8 pixels, max of 4 colors
  • For convenience, we use a square table 16x16 tiles, 128x128 pixels
  • Tiles are counted row order, 0 to 255
  • In 8x16 mode, all sprites must begin on even-number tiles

Converting to 4 Colors

  • Draw in full color for creation
  • Reduce to 4-color palette for storage
  • Apply full color palette in game

Sticking it in the Game

  • bmp2chr
  • 128x128 pixels
  • 4 colors (black, white, red, blue)
  • Creates NES CHR ROM pattern table

In-Game Palette

  • black/white/red/blue to on-screen colors
  • Setting colors
  • Multiple sprites with different colors
  • Palette index (2nd sub palette is index )
  • Palette swapping (zelda ring, mario fire)

Picking a Palette

  • Choose the 3 most important colors for your character
  • Often, entry 1 for outline, entries 2 and 3 for color
  • Good general scheme:
    • one sub palette for the hero
    • two for enemies
    • one for powerups and items

Example Palette

  • Foreground has outline color for sprites
  • Consistent gradient direction allows smooth palette changes


Frame Timing

  • vwait
  • NMI
  • Sprite 0 Hit

vwait

  • Loop until the television begins/ends the vertical retrace (port PPUSTATUS $2002)
  • Safe to write to PPU during vwait
  • Set scroll values and PPU address to 0 by end of vblank
  • Example code in provided demos

NMI

  • Enabled by bit 7 of $2000
  • Triggered at start of vblank, program automagically jumps to a specified label
  • Use resume keyword at end of NMI

Sprite 0 Hit

  • Flag cleared at end of vblank
  • Flag set when sprite 0 draws a pixel over a background pixel
    • forground not transparent, background not primary color
  • Bit 6 of $2002
  • Split screen scrolling.Wait for clear and set

http://bobrost.com/nes/lectures/NES_February_11.pdf

Good nbasic Coding

  • Goto and Gosub
    • Use goto for loops
    • Use goto for downward jumps within a single “function”
    • Use gosub/return everywhere else

Multiple Sprites

Being smart about sprite memory

  • Fill sprite memory during the frame draw, DMA during the vblank
  • We should be able to support a changing number of sprites on screen
  • But drawn sprites stay onscreen until you manually clear them
  • Keep track of what you’ve drawn in the frame, then clear the rest

Main Draw Function

  • next_sprite tells us the next unused piece of sprite memory.
  • clear_end_sprites will remove unused sprite garbage that may be left.
array absolute $4014 SPRITE_DMA
array absolute $200 spritemem

draw_stuff:
    set next_sprite 0
    gosub draw_hero
    gosub draw_enemies
    gosub clear_end_sprites
    gosub vwait
    set SPRITE_DMA 2
    return

Clearing Sprites

  • The NES always draws all 64 sprites, no matter what
  • Once you set a sprite to draw something, it will always draw that until you tell it otherwise
  • You can’t delete them, but you can make them unseen

  • Make sprites invisible
  • Set the tile to a blank tile, often 0 or 255
  • Problem: 8-sprite scanline limit
  • Problem:The tile may change with bank swapping

  • Shove them off screen!
  • The NES addresses 256 vertical pixels, but only 240 are visible
  • Set the y coordinate of unused sprites past the bottom of the screen

Backgrounds

  • 4 Screen Buffers (Name Tables) (32x30 tiles each)
  • Horizontal vs Vertical mirroring

Name Tables

  • Similar to sprites, but for the background
  • 32x30 tiles per name table
  • Each byte in the table is a reference to a tile in the pattern table
  • Start writing PPU memory to each name table starting at the base address
  • Usually row order, but register $2000 will let you write in column order

Horizontal Mirroring

  • The NES only has enough VRAM for 2 Name tables
  • Horizontal mirroring wires horizontally adjacent name tables together.
  • Good for vertical scroller games

Vertical Mirroring

  • Wires vertically adjacent name tables together
  • Good for side scroller games, like Super Mario Bros

Attribute Table

  • 32 x 30 tiles is only 960 bytes

  • 64 more and we’ve got a full kilobyte

  • Let’s invent a difficult way to set colors in the background!

  • The attribute table determine the sub palettes of name table tiles

  • Bytes are arranged row-order

  • Each byte affects a 4x4 tile area of the background

    • Affects a 4x4 tile area of the name table
  • 8 bits per byte, 2 bits to set a sub palette. A byte can set 4 sub palettes.

  • Every 2 bits affects 2x2 tiles