...menustart
- CS 98-026
...menuend
- 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.
- 64 Colors in the system palette (not fully unique)
- 16 Colors in the background palette
- 16 Colors in the foreground palette
- 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.
- 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
- NES has a fixed resolution of 256x240
- Your monitor is bigger
- Playing games in a tiny window is no fun
- hq3x seems good
- Initialization
- Set up graphics
- Set initial values
- The Game Loop
- Draw the screen
- Get player input
- Calculate the next frame
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
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 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单元
...
- 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
- 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
- bits 0-1 Sub palette
- bits 2-4 unused
- bit 5 behind background
- bit 6 flip horizontal
- bit 7 flip vertical
- 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]
- Wait for vertical retrace
- Write horizontal scroll value to $2005
- Write vertical scroll value to $2005
- 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
- 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
- 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
- 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
- Compile to “music.dat”, then assemble to “music.nes”
nesmus test.mus music.dat
...
nesasm music.asm
- Include nesmus binary data in your game
- Include nesmus interpreter in your game
- Webpage Resources section
- RTFM
- 3 colors, plus transparent
- Multiples of 8x8 pixels, often of 8x16
- 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.
- 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
- Draw in full color for creation
- Reduce to 4-color palette for storage
- Apply full color palette in game
- bmp2chr
- 128x128 pixels
- 4 colors (black, white, red, blue)
- Creates NES CHR ROM pattern table
- 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)
- 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
- Foreground has outline color for sprites
- Consistent gradient direction allows smooth palette changes
- vwait
- NMI
- Sprite 0 Hit
- 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
- 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
- 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
- Goto and Gosub
- Use goto for loops
- Use goto for downward jumps within a single “function”
- Use gosub/return everywhere else
- 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
- 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
- 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
- 4 Screen Buffers (Name Tables) (32x30 tiles each)
- Horizontal vs Vertical mirroring
- 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
- The NES only has enough VRAM for 2 Name tables
- Horizontal mirroring wires horizontally adjacent name tables together.
- Good for vertical scroller games
- Wires vertically adjacent name tables together
- Good for side scroller games, like Super Mario Bros
-
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