HuC6280 Assembly programming for the PC Engine! (TurboGrafx-16)

The PC-Engine uses a super-charged version of the 65c02... which itself is an improved version of the 6502

Known as the TurboGrafx-16, the PC-Engine is often mistaken for a 16 bit system because of its's speed ang graphical capability.

There are various sucessors to the PC engine, but we'll only be covering the basic model.

PC Engine (TurboGrafx-16)
Cpu 1.79mhz HuC6280 (7.16 MHz fast mode)
Ram 8k 
Vram 64k
Resolution 256x239 / 565x242
Sprites 64 onscreen, 16 per line (16x16 - 32x64)
Tilemap 32x32 (or higher)... max 2048 unique patterns
Colors 612 onscreen (16 per tile/sprite)
Sound chip HuC6280 6 channel wavetable synthesis
CD Optional CD drive
PSU PAD-105: 9V Center Negative (10V Famicom adaptor works)
PAD-124: 10V Center Positive
OTHERS MAY BE DIFFERENT! - see list here

ChibiAkumas Tutorials


Lesson A1 - Extra commands in the 6280 (PC Engine) processor
   
Lesson P5 - Bitmap Functions on the PC Engine (TurboGrafx-16)

Lesson P14 - Joystick Reading on the PC Engine (TurboGrafx-16)

Lesson P20 - Palette definitions on the PC Engine (TurboGrafx-16)

Lesson P25 - Sound on the PC Engine (TurboGrafx-16)




Resources


PC Engine GT Capacitor list - For repairing the portable PC Engine

The PC-Engine CPU banking and Memory Map
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)

IO Ports
From     To          Repeated
every n bytes
Purpose
$0000 $03FF VDC (Vram access and screen setup)
$0400 $07FF VCE (Palette selection)
$0800 $0BFF
Sound Processor
$0C00 $0FFF Timer
$1000 $13FF I/O Port (Joypad)
$1400 $17FF Interrupt Control
$1800 $1FFF
unused

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
$1000
Joypad (Write) ------CS C=CLR S=SEL
$1000
Joypad (Read) -C--DDDD C=Country D=Joypad Data
$1402
Interrupt Disable -----T12 T=Timer interrupt request, 1= IRQ1, 2=IRQ2
$1403
Interrupt request -----T12 T=Timer interrupt request, 1= IRQ1, 2=IRQ2
$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 Select Register xx
$0?02 ST1 xx Reg Val L=xx
$0?03 ST2 xx Reg Val H=xx

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
03


04


05 CR Control - - - IW IW DR TE TE BB SB EX EX IE IE IE IE (BB= Background on) (SB=Sprites on)
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 
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)... 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:
Address Bits Meaning
$0400 00000000 Write 0 to reset
$0402 PPPPPPPP Palette entry number - Low byte
$0403 -------P Palette entry number - High byte
$0404 GGRRRBBB Palette color - Low Byte
$0405 -------G Palette color - High Byte
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


Tilemap Definitions
As with everything else on the PC engine Vram... each tile definiton 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 transfered 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

Byte 1 Byte 2
First 16 bytes 00111100
01111111
01100011
01100011
01111111
01100011
01100011
00000000
00222200
02222222
02200022
02200022
02222222
02200022
02200022
00000000
Second 16 bytes 00333300
03333333
03300033
03300033
03333333
03300033
03300033
00000000
00444400
04444444
04400044
04400044
04444444
04400044
04400044
00000000

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

eg - lets look at a sprite, where all pixels are color 0 or color 15

First 16 bytes
(Bitplane 1)
1110000000000111
1000000100000001
1000000100000001
0000000100000000
0000000100000000
0000000100000000
0000000100000000
0000000111111100
0011111110000000
0000000010000000
0000000010000000
0000000010000000
0000000010000000
1000000010000001
1000000010000001
1110000000000111
Second 16 bytes
(Bitplane 2)
2220000000000222
2000000200000002
2000000200000002
0000000200000000
0000000200000000
0000000200000000
0000000200000000
0000000222222200
0022222220000000
0000000020000000
0000000020000000
0000000020000000
0000000020000000
2000000020000002
2000000020000002
2220000000000222
Third 16 bytes
(Bitplane 3)
3330000000000333
3000000300000003
3000000300000003
0000000300000000
0000000300000000
0000000300000000
0000000300000000
0000000333333300
0033333330000000
0000000030000000
0000000030000000
0000000030000000
0000000030000000
3000000030000003
3000000030000003
3330000000000333
Fourth 16 bytes
(Bitplane 4)
4440000000000444
4000000400000004
4000000400000004
0000000400000000
0000000400000000
0000000400000000
0000000400000000
0000000444444400
0044444440000000
0000000040000000
0000000040000000
0000000040000000
0000000040000000
4000000040000004
4000000040000004
4440000000000444

Sprites are stored in regular ram... 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 STAB, 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

STAB sprite table
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

Byte  F  E  D  C  B  A  9  8
 7  6  5  4  3  2  1  0 Notes
1 - - - - - - Y Y
Y Y Y Y Y Y Y Y Y=Ypos (64 is first visible line)
2 - - - - - - X X
X X X X X X X X X=Xpos (32 is first visible line)
3 - - - - - A A A
A A A A A A A A A=Address (Top 10 bits $trueaddress>>5 )
4 YF - YS YS XF - - XS
F - - - P P P P YF=Yflip XF=Xflip YS=Ysize XS=Xsize
F=Foreground (infront of tilemap) P=Palette



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.

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
3 $0803 Frequency H 0-5 - - - - H H H H
4 $0804 Channel On/Write 0-5 E D - V V V V V Enable/data addr... reset or DirectDA… Volume
5 $0805 LR Volume 0-5 L L L L R R R R L/R Volume
6 $0806 Waveform Data 0-5 - - - W W W W W Wave data (write 32 times)
7 $0807 Noise Enable 0-3 E - - N N N N N Enable noise… Noise freq
8 $0808 LFO Freq All F F F F F F F F lfo Frequency
9 $0809 LFO Control All T - - - - - C C lfo Trigger… Control
    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
 

View Options
Default Dark
Simple (Hide this menu)
Print Mode (white background)

Top Menu
Youtube channel
ASM Programming Forums
GitHub
Dec/Bin/Hex/Oct/Ascii Table

Z80 Content
Learn Z80 Assembly
Hello World
Advanced Series
Multiplatform Series
Platform Specific Series
ChibiAkumas Series
Grime Z80
Z80 Downloads
Z80 Cheatsheet
Sources.7z
DevTools kit
Z80 Platforms
Amstrad CPC
Elan Enterprise
Gameboy & Gameboy Color
Master System & GameGear
MSX & MSX2
Sam Coupe
TI-83
ZX Spectrum
Spectrum NEXT
Camputers Lynx

6502 Content
Learn 6502 Assembly
Advanced Series
Platform Specific Series
Grime 6502
6502 Downloads
6502 Cheatsheet
Sources.7z
DevTools kit
6502 Platforms
Apple IIe
Atari 800 and 5200
Atari Lynx
BBC Micro
Commodore 64
Commander x16
Super Nintendo (SNES)
Nintendo NES / Famicom
PC Engine (Turbografx-16)
Vic 20

68000 Content
Learn 68000 Assembly
Platform Specific Series
Grime 68000
68000 Downloads
68000 Cheatsheet
Sources.7z
DevTools kit
68000 Platforms
Amiga 500
Atari ST
Neo Geo
Sega Genesis / Mega Drive
Sinclair QL (Quantum Leap)
X68000 (Sharp x68k)

My Game projects
Chibi Aliens
Chibi Akumas

Work in Progress
Learn 6809 Assembly
Learn 65816 Assembly
Learn 6809 Assembly
Learn PDP11 Assembly
Learn TMS9900 Assembly
Learn 8086 Assembly (x86)
Learn Risc-V Assembly
Wonderswan
MsDos
Learn ARM Assembly
Dragon 32/Tandy Coco
Ti 99
Gameboy Advance
Risc Os

Misc bits
Ruby programming




Chibi Akumas V1.666 has taken over 350 hours of development, if you want to support my work, and learn all the secrets of the game's development, please back me on patreon!





Thanks to Homebrew Legends for help promoting my game!
Buy Chibi Akuma(s) from PolyPlay
Buy ChibiAkuma(s) games now!