HuC6280 Assembly programming for the PC Engine!

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
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

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)

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
$0000 ST0 xx Select Register xx
$0002 ST1 xx Reg Val L=xx
$0003 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

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