|The NES was the
console I owned, compared to the 8 bits I was used to (Like the CPC)
it's incredible high speed, and smooth scrolling were really impressive.
While it's games tend to be limited to platformers, it's hardware was very decent for the time, and in it's later years, it's low cost made it an easy choice as a home games system.
These days the main 'pulling point' for new developers is the huge number of sales the system had, and its prescence in pretty much every territory, meaning that if you want to get into retro development, and get noticed, the NES or Famicom (as it was called in Japan) is a great computer to look at!
The NES CPU is based on the 6502, and it's almost the same, however it has no BCD (Binary Coded Decimal) mode, however it's generally the same as the 6502
on consoles usually do not
work like they do on computers like the BBC
Graphics are not just 'bytes' in a memory address... The screen is made up of a 'Tile Layer' and a 'Sprite Layer'
To explain Tiles and sprites, lets look at our imaginary game shown to the right 'The Super Yuusha Siblings', on a theoretical game system the 'GameChibi'... Just to be very clear, we looking at this as a concept, not the actual layout of the NES!
Looking at our example, We have a level with some grass, blocks, and some collectable 'stars'... our hero, Yume is controlled by the player...
|The screen is made
up of the Tile layer, and the Sprite
Usually Sprites are drawn above the Tiles... but sometimes they may be drawn below.
It's also possible we could use sprites for the stars.. but sprites are very limited, so the object doesn't move, then tiles will do the job... we can even animate the stars by switching the tile between different patterns
|The tile array on
the systems we'll be looking at is
made up of 8x8 tiles... the array is a 'grid' of these tiles, so the
tiles must line up, a block cannot be at a 'half way boundary'
Tiles are defined by a number (usually 0-255)... we define the bitmap (image) data for that tile (we'll call it a pattern), then tell the hardware what positions in the tile array to use that pattern.
In our example, the black background is pattern 0 ... the blocks are pattern 1... the grass is pattern 2... and the stars are pattern 3
Our 'GameChibi' console has a tile array of 8x8... and 64 bytes is used to define the tile grid...
So to define the stars, we need to set memory locations 10,20 and 15 of the tile array to byte '3'
A real system usually has a tile array bigger than the screen (maybe just by one row and one coumn)... this is to allow smooth scrolling of the screen, where two tiles are 'half shown'
Now in the case of our 'GameChibi' system, with it's 64 tile screen, and 256 pattern definitions, we could just set every visible tile to a different pattern, and treat the screen as a plain bitmap again... We can do that on the MSX1, Unfortunately, many systems do not have enough tile patterns to do this, and it's often too slow anyway... we really have to work with the system in the 'way it wants' to get good results.
|Because our system is using hardware sprites, we have
to design our game sprites in a way that can be drawn with the hardware
sprites... for example lets look at our Yume sprite... if our
'GameChibi' used 8x8 hardware sprites, we would have to use 48 of them
to make this image!... we can save 6 (marked green)... these have no
data, so we can just not draw them...
When it comes to moving our character, the software will have to move the hardware sprites all together, so the user does not realise they are made up of many sprites!... on systems with more onscreen colors than sprite colors, two or more sprites may be overlapped to make the sprite appear more colorful
Most systems will have one color (usually 0) which marks the transparent colour.
but there is a problem! with software sprites on a bitmap screen, we can draw as much as we want, it will just get slow.... but with hardware sprites, we have a fixed limit of how many sprites can be shown onscreen at once! sounds bad? well actually it's worse, even though a system like the NES can show 64 sprites onscreen, there can only be 16 on a line... if more than 16 appear on the same line, some will flicker, or not appear... there's nothing we can do about it, we just have to design our game to avoid this problem!
|The NES memory map
combination of RAM, cartridge based hardware, and memory mapped
registers... our main program will start at $C000... and the first
$8000 bytes of memory are the system's 2k of ram.
$2000-4020 have various 'Ports' that are used to read and write the hardware of the nes.
Fortrunately the NES CPU doesn't have any real surprises for us, although it lacks the 6502's decimal mode, it is a pretty regular 6502... there's no 'Weirdness' like the PC-Engine's extra commands, and the ZeroPage is in the 'proper place'
|&0000||4||Header - do not change!||db "NES",$1a|
|$0004||1||Rom pages (16k each)||db $1|
|$0005||1||CHR-Rom Pages (8k each)||db $0|
= mapper no bottom 4 bits , Four screen vram layout, Trainer at
Battery ram at &6000, Mirror (0=horiz, 1=vert)
|$0007||1||mmmm--PV||mmmm= mapper top 4 bits... Pc10 arcade, Vs unisystem||db %00000000|
|$0008||1||RAM pages (8k each)||db 0|
|the PPU is the NES
and Famicom's Graphics system, it's
controlled by 8 registers between $2000 and $2007... we use these to
check and set attributes of the system. and write to VRAM (which isn't
in the normal memory map!)
Strangely, when we want to write to VRAM, it's in Big Endian mode - so we have to send the High byte, then the Low byte... the opposite of the normal 6502!
B=Back Pattern Table (0/1)
P=sprite Pattern table
I=Increment vram address
AA=Name Table address
|$2002||PPUSTATUS||Read resets PPUSCROLL|
|$2004||OAMDATA||Sprite data (to write to addr, autoincs)|
|Select X offset and Y Offset|
|Select Address to write to (Big Endian!)||Write resets PPUSCROLL|
|$2007||PPUDATA||BBBBBBBB||Byte to write to address in $2006|
|Memory in the Vram is not entirely fixed in purpose,
this means you can have weird effects, like usi
Specify a memory address by writing the byte pair to $2006... HIGH BYTE FIRST... (Big Endian)
NOTE: Writing to VRAM outside of VBLANK will cause problems... also note, selecting an address resets PPUSCROLL
EG, lets point to $3F00... and write $11 to the first palette entry!
lda #$3F ;High byte
sta $2006 ;Send to vram select
lda #$00 ;Low byte
sta $2006 ;Send to vram select
lda #$11 ;New value
sta $2007 ;Send to write data
|First 8 bytes||00111100
|Second 8 bytes||00222200
|4000h||APU Channel 1 (Rectangle) Volume/Decay (W)||CCLEVVVV||Volume, Envelope Length counter,duty Cycle|
|4001h||APU Channel 1 (Rectangle) Sweep (W)||EUUUDSSS||Sweep, Direction,Upadte rate, Enabled|
|4002h||APU Channel 1 (Rectangle) Frequency (W)||LLLLLLLL||frequency L byte|
|4003h||APU Channel 1 (Rectangle) Length (W)||CCCCCHHH||frequency H byte, length Counter load register|
|4004h||APU Channel 2 (Rectangle) Volume/Decay (W)||CCLEVVVV||Volume, Envelope Length counter,duty Cycle|
|4005h||APU Channel 2 (Rectangle) Sweep (W)||EUUUDSSS||Sweep, Direction,Upadte rate, Enabled|
|4006h||APU Channel 2 (Rectangle) Frequency (W)||LLLLLLLL||frequency L byte|
|4007h||APU Channel 2 (Rectangle) Length (W)||CCCCCHHH||frequency H byte, length Counter load register|
|4008h||APU Channel 3 (Triangle) Linear Counter (W)|
|4009h||APU Channel 3 (Triangle) N/A (-)|
|400Ah||APU Channel 3 (Triangle) Frequency (W)|
|400Bh||APU Channel 3 (Triangle) Length (W)|
|400Ch||APU Channel 4 (Noise) Volume/Decay (W)||CCLEVVVV||Volume, Envelope Length counter,duty Cycle|
|400Dh||APU Channel 4 (Noise) N/A (-)|
|400Eh||APU Channel 4 (Noise) Frequency (W)||LLLLLLLL||frequency L byte|
|400Fh||APU Channel 4 (Noise) Length (W)||CCCCCHHH||frequency H byte, length Counter load register|
|4010h||APU Channel 5 (DMC) Play mode and DMA frequency (W)|
|4011h||APU Channel 5 (DMC) Delta counter load register (W)|
|4012h||APU Channel 5 (DMC) Address load register (W)|
|4013h||APU Channel 5 (DMC) Length register (W)|
|4014h||SPR-RAM DMA Register (W)|
|4015h||DMC/IRQ/length counter status/Sound channel enable register (RW)||DF-54321||Dmc irq status/ Frame irq status Channel 12345 on|
|4016h||Joypad #1 (RW)|
|4017h||Joypad #2/APU SOFTCLK (RW)|
|The NES Name Table
defines the Tilemap's patterns... one byte per 8x8
tile defines the number of the tile... the name table is 32x30 tiles in
size, so spans from $2000-$23BF
Note: Name Table 2,3 ($2800-$3000) are not available on an standard NES , they will only be available if your cartridge has Extra Ram!
Color's are defined by the 'Attribute table'... effectively, each square block of 2x2 tiles (16x16 pixels) have to use the same color palette... and each block of 4x4 tiles (32x32 pixels) are defined by a single byte (2 bits per block - for 4 possible palettes)
Lets take the example to the right, with different 16 tiles making up a grid of 32x32 pixels- each areas palette would be defined by a single byte in the way below:
|3||Attribs||VHB---PP||Vflip Hflip Background priority Palette|
|The Nes palette is
slightly odd compared to RGB systems, you can see how the HEX values
map to colors in the chart to the right->
Each tile has 4 colors, and you can have 4 separate palettes for tiles... and a separate 4 for sprites... this makes a total of 32 color definitions for tiles and sprites combined