The PC engine uses an effective 21
bit memory map... 13 bits of the address specified, and 8 bits
from the MPR register
The topmost 3 bits are mapped through a Memory Management unit which
maps to Ram/Rom or hardware
This is all a bit confusing, but it's really pretty easy
This Memory management unit has 8 registers, each of which handles a
range of $2000 of the memory map, and a bank from $00-$FF... $FF is
the hardware I/O ... $F8 is the 8k of basic ram... $00 is the first
page of your cartridge rom.
To page in a bank, we need to specify it by 'bit position' (so bank
7 has bit 7 set... so 128)
we load A with the bitmask for that MPR, then use the special
command TAM (transfer A to MPR)
eg: - lets load bank $F8 into MPR page 1 ($2000-$3FFF)
lda #$F8 ;ram bank
TAM #2 ;bit 2 for bank 2
(%00000010)
NOTE: The ZeroPage is at $2000 , and the Stack is at $2100
... this is unaffected by the MPR's.... this causes problems in
VASM, where if we try to write STA $0000 ... the assembler
will optimize it to STA $00
MPR
Setting
From
To
MPR Page
0
1
$0000
$1FFF
$FF (I/O)
1
2
$2000
$3FFF
$F8 (RAM)
2
4
$4000
$5FFF
????
3
8
$6000
$7FFF
????
4
16
$8000
$9FFF
????
5
32
$A000
$BFFF
????
6
64
$C000
$DFFF
????
7
128
$E000
$FFFF
$00 (Card Rom)
Segments
Bank
Purpose
$FF
I/O
$F9-$FB
SuperGrafx extra RAM
(24k)
$F8
PC Engine RAM (8k)
$F7
Savegame RAM
$00-$F6
HuCard ROM
Vectors
From (Logical)
To (Logical)
Purpose
$FFF6
$FFF7
IRQ2 (External) / BRK
$FFF8
$FFF9
IRQ1 (VDC / Vblank)
$FFFA
$FFFB
Timer interrupt
$FFFC
$FFFD
NMI
$FFFE
$FFFF
-
From (Physical)
To (Physical)
Purpose
$00 1FFE
$00 1FFF
Reset
The Reset vector should be at $5FFE in your rom, and should contain "DW
$E000" to start your program
IO Ports
Address
Purpose
Bits
Detail
$0000
GPU Reg Select (ST0)
NNNNNNNN
reg N
$0002
GPU Data L (ST1)
LLLLLLLL
Data L
$0003
GPU Data H (ST2)
HHHHHHHH
Data H
$0400
Palette: Reset
00000000
Write 0 to Reset
$0402
Palette: Palette Entry L
PPPPPPPP
Palette num (0-511)
$0403
Palette: Palette Entry H
-------P
Palette num (0-511)
$0404
Palette: New Color L
GGRRRBBB
Color
$0405
Palette: New Color H
-------G
Color
$0800
Channel Select
-----CCC
Channel Select
$0801
Main Amplitude Level
LLLLRRRR
L/R Volume
$0802
Frequency L
LLLLLLLL
Frequency botttom 8 bits
$0803
Frequency H
----HHHH
Frequency top 4 bits
$0804
Channel On/Write
ED-VVVVV
Enable (play)/write data... Direct digitaldata�
channel Volume
T=Timer interrupt request, 1= IRQ1 (Vblank),
2=IRQ2 (1=occurred)
Write any values to clear Timer interrupt
(Vblank is cleared by reading VDC Status at
$0100)
$1Ax0-$1Ax1
Data Port 0/1
DDDDDDDD
4 versions... Reg x (0-3)
$1Ax2-$1Ax4
Base address (24 bit)
$LLMMHH
$1Ax5-$1Ax6
Offset Register
$LLHH
$1Ax7-$1Ax8
Increment Register
$LLHH
$1Ax9
Control Register
SOOIoiNA
A=AutoInc N=iNdex offset + base i=increment is signed o=offset is
signed
I=apply increment to base(1) or offset(0)?
OO=offset mode(None/Port0/Port1/Port0+1) S=Dataport size(0=Byte
1=Word)
$1AxA
Manual Register
Add offset to base
$1AE0-$1AE3
$LLMMHHUU
Work Register (32 bit)
$1AE4
Shift Register
----SSSS
Signed Shift for work register
$1AE5
Rotate Register
----RRRR
Signed Rotate for work register
$1AFE
AC card version
DDDDDDDD
$1AFF
AC ID
DDDDDDDD
$51=Present
$1C00
Timer Reload
-CCCCCCC
C=Counter
$1C01
Timer Control
-------S
Timer Start/Stop
The PC-Engine Graphics
system
The PC Engine graphics hardware has 64k of ram... but it's
designed for 128k... it's controlled by a set of registers... we
have 3 hardware ports we use to control the hardware... these are
usually memory mapped to $0000 ... but we also have special commands
to quickly write fixed values to the graphics system
We ALWAYS write data to the graphics system registers in HL byte
pairs... Little Endian, so low byte first.
To write data to the memory we set the address we want to write to
with MAWR... but please note , we're only setting the last 16 of the
17 bits... for example, if we set MAWR to $3FFF (%11111111111111),
the actual memory address will be %111111111111110
The effect is, that there are 2 bytes at VRAM $0000... and two
bytes at VRAM $0001 ... and these bytes DO NOT OVERLAP!
Memory address
Command
Purpose
$0?00
ST0
xx
W=
Select Register xx
R = Status (%---------BVDSLOC)
B=Busy V=Vblank D=Dma end
S=SATB transfer end L=scanLine interrupt
O=sprite Overflow (>16
sprites on a line) C=sprite Collision)
$0?02
ST1
xx
RW=
Reg Val L=xx
$0?03
ST2
xx
RW=
Reg Val H=xx
Reading from Status register (eg LDA $0100) clears Vblank interrupt
- otherwise it will 'refire'.
Lets write &6543 to 'Byte Pair' at memory address &1234 (remember we
always write in pairs):
st0 0
;Select Register 0
- to select memory address to write to
st1 $34
;Low byte of the memory address we
want to write
st2 $12
;High byte of the memory address we
want to write
st0 2
;Select Register 2
- to actually write data
st1 $43
;Low byte of data to put in memory
st2 $65
;High byte of data to put in memory
Graphics Registers
Reg
Name
Meaning
Bits
00
MAWR
Memory Address Write
01
MARR
Memory Address Read
02
VRR/VWR
Vram Data Write / Vram Data Read
(AutoIncs
after Write)
03
Unused
04
Unused
05
CR
Control
- - - IW IW DR TE TE BB SB EX EX IE IE IE IE
BB= Background on
SB=Sprites on...
IE bits 3-0 = Vblank / ScanLine match / SpriteOverflow / Collision
06
RCR
Scanning Line Detection
07
BXR
BGX Scroll
08
BYR
BGY Scroll
09
MWR
Memory Access Width
- - - - - - - - CM SCR SCR SCR SM SM WV WV (SCR=Screen Width /Height %YXX)
0A
HSR
Horizontal Sync
0B
HDR
Horizontal Display
0C
VPR
Vertical Sync
0D
BDW
Vertical Display
0E
BCR
Vertical Display End Position
0F
DCR
Block Transfer Control
10
SOUR
Block Transfer Source Address
11
DESR
Block Transfer Destination Address
12
LENR
Block Transfer Length
13
SATB
VRAM-SATB Block Transfer Source
VRAM Layout
Memory in the VRAM is not entirely fixed in purpose, this means
you can have weird effects, like using the same memory area for your
Tilemaps - and tile definitions (Patterns)... this is totally
useless, as one will corrupt the other,
but we have to understand it's possible to understand the memory...
as stated before, each address has 2 bytes...
A Pattern (tile definition) is 32 bytes in size (4 bitplanes, 8
lines)... and because each memory address in the VRAM map contains 2
bytes... the pattern will take up 16 memory addresses... this means
tile 0 starts at $0000... and tile 1 is at $0010
NOW... the TileMap has to be at $0000 .. and it takes AT LEAST $0400
(it's minimum size is 32x32, and each definition takes 2 bytes)...
so we can't use tiles 0-64
for ease, it's probably easiest to start your pattern definitions at
no 256 (memory address $1000)
Vram From
Vram To
Purpose
$0000
$03FF
Min Tilemap (Tiles 0-63)
$0400
$0FFF
Possible Tilemap (Tiles 64-255)
$1000
$7FFF
Tiles 256-2048
$7F00
$7FFF
SATB
sprite
table
$8000
$FFFF
PC-Engine only has 64k, so this is unused
Palette Definitions The PC engine uses two banks of 16 palettes of 16 colors each
... the first bank is for the tilemap (0-255), the second bank is for the
sprites (256-511)
The Palette entries are controlled by special ports in the IO range in
standard memory:
We write to $0402 and $0403 to select a palette entry, then define the new
color for the palette entry by a 16 bit definition written to $0404 and
$0405
F
E
D
C
B
A
9
8
7
6
5
4
3
2
1
0
-
-
-
-
-
-
-
G
G
G
R
R
R
B
B
B
Port
Purpose
7
6
5
4
3
2
1
0
$0400
Write 0 to
Reset
0
0
0
0
0
0
0
0
$0402
Palette Entry
L
P
P
P
P
P
P
P
P
$0403
Palette Entry
H
-
-
-
-
-
-
-
P
$0404
New Color L
G
G
R
R
R
B
B
B
$0405
New Color H
-
-
-
-
-
-
-
G
Palette
Entry
Purpose
0-255
Background
256-511
Sprites
Tilemap Definitions
As with everything else on the PC engine Vram... each tile
definition takes one memory address, which contains 2 bytes...
The top 4 bits pppp
define a 16 color palette number from 0-15...
The remaining 12 bits nnnn
nnnnnnnn define the tile number -
as stated, the first 64-256 probably can't be used because they
overlap the tilemap... no's 2048-4095 CANNOT be used, as the memory
these would use would be 64k-128k... and this memory is not
installed in the PC Engine.
ppppnnnn nnnnnnnn
Tile definitions Tile definitions use 4 bitplanes for 16 colors, and tile
definitions are 8x8 - so 32 bytes ... Data is transferred in Words, and
rather strangely we send bitplane 1+2 of lines, one at a time... then we do
the same for bitplanes 3 and 4
Sprite Definitions
The basic sprite size is 16x16, though larger sprites can be created by
tilling them, for up to 32x64.... only neighboring sprites can be tilled.
Sprites are NOT in the same format as the tilemap, they are 16x16 with 4
bitplanes, but each plane is sent separately
For Example lets look at a sprite, where all pixels are color 0 or
color 15!
Sprites are stored in regular VRAM... the sprite definitions are stored in
special ram which we CANNOT ACCESS...however we can allocate a bank of 256
addresses (each containing one word) called SATB, and then get the hardware
to copy that ram to the special ram... it's suggested you use $7F00 for that
purpose.
To start the copy we just write the address to control reg $13
SATB - Sprite attribute
table buffer
The Sprite table allows for up to 64 sprites... each one has 4 words of data
- making 256 words in total... the format is as follows
Joypad port at $1000
Joypad reading is performed with Port $1000.... This port uses 4 bits
for reading... so two reads from this port are needed to get the 8 buttons
of a joypad... The PC Engine is capable of supporting up to 5 joypads,
though we'll only read in two in these tutorials
Before we can start reading, we need to initialize the 'Multitap' (the
hardware that toggles the joypads)... to do this we just write a #1 then
#3 to port $1000... we need a short delay after each write....
Once the Multitap is initialized, we can get the button states by
alternating writes of #1 and #0 to port $1000 - this will return the buttons
of all 5 joysticks in order...
Joypad
Select bit
(Bit 0 $1000)
7
6
5
4
3
2
1
0
1
1
CD addon
(1=yes)
Country
(0=jpn)
-
-
Left
Down
Right
Up
1
0
CD addon
(1=yes)
Country
(0=jpn)
-
-
Run
Start
B
A
2
1
CD addon
(1=yes)
Country
(0=jpn)
-
-
Left
Down
Right
Up
2
0
CD addon
(1=yes)
Country
(0=jpn)
-
-
Run
Start
B
A
The PC Engine
actually supports 5 joypads, but Joypads 3-5 work in exatly the
same way, we just need to keep reading in from the same port.
The PSG Sound generator
The PC Engine PSG has 6 wave based
sound channels... each one uses 32 wave samples, of 5 bits each.
We have to snd some commands to $0804 to tell the PSG we're going to
write data.. then send the data to $0806
We also need to set the volumes correctly!
To the right is a working example which will play a sound wave.
lda #0
sta $0800;Channel Select
lda #255
;Mixing
sta $0801
lda #1 ;Tone L
sta $0802
lda #10
;Tone H
sta $0803
lda #%00011111
;Chanel Op - Set 'Data Write'
sta $0804
lda #%01011111 ;Chanel
Op - Set 'Reset Write Address'
sta $0804
lda #255
sta $0805 ;LR Volume
ldy #4
ChibiSoundMoreWaves:
lda #%00011111
sta $0806
sta $0806
sta $0806
sta $0806
lda #%00000000
sta $0806
sta $0806
sta $0806
sta $0806
dey
bne ChibiSoundMoreWaves
lda #%10011111
;Chanel Op - Set 'Play'
sta $0804
rts
Sound Registers
The PSG
is controlled by 10 registers... first a channel should be selected with
Register 0... Channels are numbered 0-5 (written 1-6 in the manuals)
Reg
Address
Meaning
Channels
7
6
5
4
3
2
1
0
Bit
Meaning
0
$0800
Channel Select
All
-
-
-
-
-
C
C
C
Channel Select
1
$0801
Main Amplitude
Level
All
L
L
L
L
R
R
R
R
L/R Volume
2
$0802
Frequency L
0-5
L
L
L
L
L
L
L
L
Frequency
botttom 8 bits
3
$0803
Frequency H
0-5
-
-
-
-
H
H
H
H
Frequency top 4
bits
4
$0804
Channel
On/Write
0-5
E
D
-
V
V
V
V
V
Enable
(play)/write data... Direct digitaldata� channel Volume