65c02 Assembly programming for the Commander x16

*** Warning - The X16 is a prototype - it's hardware could change at any time*


*** Double Warning - V31 of the emulator changed many video register addresses...
sources.7z has been updated, but documentation on this site will not be updated until emulator reaches final state*

The Commander x16 is a FPGA enhanced recreation of the C64, adding high resolution graphics, Tilemap support and hardware sprites it gives the 6502 programmer the opportunity to develop for a more modern system with the familiarity of the C64.

The commander X16 is still under construction - the developers have a facebook group here

Commodore 64 Commander x16
Cpu 1mhz 6510 (6502 base) 8mhz 65x02
Ram 64k 512k+
Sprites 8 per line (24x21 px) 128 sprites
8x8 to 64x64
Resolution 320x200 2 color
160x200 4 color
Up to 640x480 256 color
Sound chip SID 2x AY-3-8910 + Digital Sound

Links
x16Emu
- Emulator for the x16
Documentation - Hardware notes
Sample code - Coding Examples

New Ports
The 'Vera' Graphics

Address Name Bits Details
$9F20 VERA_ADDR_HI IIIIHHHH 20 Bit address I=Increment
$9F21 VERA_ADDR_MID MMMMMMMM 20 Bit address
$9F22 VERA_ADDR_LO LLLLLLLL 20 Bit address
$9F23 VERA_DATA1 DDDDDDDD Data Port 1
$9F24 VERA_DATA2 DDDDDDDD Data Port 2
$9F25 VERA_CTRL R------A R=Reset A=Address (port 1 / 2)
$9F26 VERA_IEN -----SLV Interrupt enable - S=Sprite L=Line V=Vsync
$9F27 VERA_ISR -----SLV Interrupt Occurred - S=Sprite L=Line V=Vsync

VRAM

To access this data - write an address to $9F20-$9F22, then read or write with port $9F23

Group Address range
Description
Screen Ram $00000 $1FFFF
Video RAM

$20000 $207FF
PETSCII character ROM (upper-case)

$20800 $20FFF
PETSCII character ROM (lower-case)
L1 $40000 L1_CTRL0 MMM----E M=Mode 7=8bpp bmp / E=Enable
L1 $40001 L1_CTRL1 --HWhhww H=tile Height / W=tile Width / m=map height / w=map width
L1 $40002 L1_MAP_BASE_L LLLLLLLL Map Base (9:2)
L1 $40003 L1_MAP_BASE_H HHHHHHHH Map Base (17:10)
L1 $40004 L1_TILE_BASE_L LLLLLLLL Tile Base (9:2)
L1 $40005 L1_TILE_BASE_H HHHHHHHH Tile Base (17:10)
L1 $40006 L1_HSCROLL_L LLLLLLLL Hscroll (7:0)
L1 $40007 L1_HSCROLL_H ----HHHH Vscroll (11:8)

Alternate L1_BM_PAL_OFFS ----BBBB In mode 567 Bmp palette offset
L1 $40008 L1_VSCROLL_L LLLLLLLL Hscroll (7:0)
L1 $40009 L1_VSCROLL_H ----HHHH Vscroll (11:8)
L1 $4000A $4000F
Layer 1 registers
L2 $40010 L2_CTRL0 MMM----E (M=Mode E=Enable)
L2 $40011 L2_CTRL1 --HWhhww H=tile Height / W=tile Width / m=map height / w=map width
L2 $40012 L2_MAP_BASE_L LLLLLLLL Map Base (9:2)
L2 $40013 L2_MAP_BASE_H HHHHHHHH Map Base (17:10)
L2 $40014 L2_TILE_BASE_L LLLLLLLL Tile Base (9:2)
L2 $40015 L2_TILE_BASE_H HHHHHHHH Tile Base (17:10)
L2 $40016 L2_HSCROLL_L LLLLLLLL Hscroll (7:0)
L2 $40017 L2_HSCROLL_H ----HHHH Vscroll (11:8)

Alternate L2_BM_PAL_OFFS ----BBBB In mode 567 Bmp palette offset
L2 $40018 L2_VSCROLL_L LLLLLLLL Hscroll (7:0)
L2 $40019 L2_VSCROLL_H ----HHHH Vscroll (11:8)
L2 $4001A $4001F
Layer 2 registers
SprCom $40020 SPR_CTRL -------E E=Enable
SprCom $40021 SPR_COLLISION ----CCCC C=Collision Mask
SprCom $40022 $4002F
Sprite control registers
DspCom $40040 DC_VIDEO F----COO F=current field (RO) C=Chroma disable O=Out mode (1=vga)
DspCom $40041 DC_HSCALE HHHHHHHH 128=Normal 64=2x
DspCom $40042 DC_VSCALE VVVVVVVV 128=Normal 64=2x
DspCom $40043 DC_BORDER_COLOR CCCCCCCC
DspCom $40044 DC_HSTART_L HHHHHHHH hstart bit 0-7 (0)
DspCom $40045 DC_HSTOP_L LLLLLLLL hstop bit 0-7 (640)
DspCom $40046 DC_VSTART_L HHHHHHHH vstart bit 0-7 (0)
DspCom $40047 DC_VSTOP_L LLLLLLLL hstop bit 0-7 (480)
DspCom $40048 DC_STARTSTOP_H --VvHHhh V=vstop Bit 8 / v=vstart bit 8 / H= hstop bit 8 / h=hstart bit 8
DspCom $40049 DC_IRQ_LINE_L LLLLLLLL
DspCom $4004A DC_IRQ_LINE_H -------H
DspCom $4004B $4005F
Display composer registers
Palette $40200 Color 0 Green/Blue GGGGBBBB G=Green / B=Blue
Palette $40201 Color 0 Red ----RRRR R=Red
Palette $40202 $403FF ... Palette 1-256
SprData $40800 Sprite 0: Address L LLLLLLLL L = Vram Address L
SprData $40801 Sprite 0: Address H M---HHHH H=Vram Address H / M= Mode (0=4bpp 1=8bpp)
SprData $40802 Sprite 0: Xpos L XXXXXXXX Xpos bits 0-7
SprData $40803 Sprite 0: Xpos H ------XX Xpos bits 8-9
SprData $40804 Sprite 0: Ypos L YYYYYYYY Ypos bits 0-7
SprData $40805 Sprite 0: Ypos H ------YY Ypos bits 8-9
SprData $40806 Sprite 0: Settings 1 CCCCZZVH C=Collision mask Z=Z depth V=Vflip H=Hflip
SprData $40807 Sprite 0: Settings 2 HHWWPPPP H=Height , W=Width (0/1/2/3 = 8/16/32/64 px) / P=Palette offset
SprData $40808 $40FFF ... Sprite data 1-127



256 color Hello World on the Commander x16 with the x16emu

Lets create a Hello World example, using a bitmap font in 256 color mode.

x16_HelloWorld.asm

Compiling a program for the x16
When it comes to compiling a x16 program, we just need to build a regular C64 PRG file... we can compile a valid file with VASM...

vasm6502_oldstyle_win32.exe %1 -c02 -cbm-prg -chklabels -nocase -Dvasm=1  -L \BldC64\Listing.txt -DBuildC64=1 -Fbin -o "\BldC64\Program.prg"

The only addition to my regular C64 script is the -c02 switch... this enables the extra 65C02 commands (rather than 6502)
To start our program we can just specify our PRG on the command line to x16emu... the script below will load a compiled program 'Program.prg' in the folder \BldC64\

x16emu.exe -run \BldC64\Program.prg

Using 256 color BMP screens...

The x16 can run .PRG files in the same way as a C64... we're creating a generic header here for
We're going to need some bytes of the zero page, to make them easier, we'll give them pair names...

We define them with EQU statements
We're going to need access to the new hardware using ports $9F20-$9F27

We need 3 'address' ports between $9F20-$9F22 ... these define the 20 bit VRAM address we want to write to.

When we write to $9F23 the data we write will be written to VRAM... as long as increment is enabled (with the top nibble of $9F20) we can keep writing more bytes to the screen...

We also need the control port to set up the Address port (we only use this once!)
First we need to set up our port... we're going to use port 1 at adress $9F23...

We need to set up the Address ports at ($9F20-#9F22) to control port 1... we do this by setting Bit 0 to 0 and writing to 'Vera_Ctrl' at $9F25
We're going to need to set our VRAM address a lot! we'll use the AYX registers to set the VRAM HML writing address...

Don't forget, we'll probably want to set the top nibble of A to $1- to ensure the address auto increments with each write!
OK, we're ready to start writing... first we need to set up our screen layout... we need to write to VRAM addresses $40040-$40042

$40040 will turn on the screen for us...

We'll use $40041 & $40042 to set the screen size

The result? a 320x240 screen of 256 colors!


We're going to set up our palette... we do this by writing to VRAM address $40200 onwards...

The code here will copy 256 color entries to VRAM.

Each palette entry takes 2 bytes... one nibble per byte.
Because the 65c02 is little endian, the first byte is $GB, the second is $-R

The sample palette shown will set up a CPC type color palette... with a blue background, and yellow text!

(Note... the palette screenshot is abridged... there's more test colors in the sourcefile)



OK, we've got our palette set up, but now it's time to turn on our screen and set it up...

We set up the screen as an 8 bit per pixel (256 color) bitmap screen by setting the top 3 bits of $040000 - we also set the bottom bit 0 to turn on the screen

We also reset all the other parameters, we write zeros to $040001-$040009
Our screen is now set up, so lets test it!

Each pixel of the screen is 1 byte, and the screen starts at vram address $0000... each line is 320 bytes wide and the screen is 240 lines tall, so the screen is 76,800 bytes in size

To test the screen, and see our palette, we'll fill the entire screen with concecutive bytes
The results can be seen to the right!

Printing characters to the screen...

We're going to define another function to set the VRAM write address... AddVHML_AYX will add AXY (or just XY) to the address we're writing to...

We'll use this during calculations of addresses, and to move down a line after drawing parts of our characters
OK, lets start printing our character!

We're using a 1bpp font (as seen in the regular tutorials)... but our font has no characters below 32... so first we subtract 32 from our character number/

Each 1bpp character is made up of 8 bytes, so we multiply the number of the character we want to show by 8, and add the base address of the BitmapFont... we store the result in zero page z_hl ... this is our Source bitmap font data.
We've calculated our source address, now it's time to calculate the destination.

We're using two bytes in RAM defined as CursorX and CursorY... these are the X,Y position of our 'Cursor' in characters...

We need to calculate the address of the character position... to do this we need to use the following formula

Address = VRAM Base + CursorY * FontHeight * ScreenWidth + CursorX * FontWidth

So

Address = 0 + CursorY * 8 * 320 + CursorX * 8

We achieve these multiplication by a series of bitshifts and additions... using z_de as a temporary holder during the calculations...

once we've calculated the Y part, we do the same for the X part, and add the two together... we have to ensure we pass any carry to the higher byte...

Once we're done, we use SetVeraHML to set the address from AYX
OK, we're going to need to copy the data from z_HL, and write it to V_D1....

The source data is 1bpp... the destination data is 8bpp

So, we read in one byte from z_HL - store it into z_As, then we pop each of the bits off the left of the byte... writing each to the screen data...

We use X as a pixel counter for the line, so we repeat this pixel shifting 8 times
Ok, we've done one line of our font... so we need to move down a line

Each line is 320 bytes, BUT our character is 8 - so we add 320-8 to the memory address to calculate the next line.

We use Y as a counter for the line (and an offset for the font source address) we INC Y until it reaches 8

Once we've finished drawing our character, we increase our CursorX... we check if we've gone over the 40 character wide screen, if it has we use our NextLine function to move down a line
We need some functions to complete the code - these are almost the same as the c64 originals (we now use 65c02 command STZ)
The results can be seen here..

In this example, we've use the PrintChar function to run a monitor tool to show the registers and memory... this will help with future development on the x16!


X16:2 - 2x 16 color Layers on the Commander x16

When we looked at the last episode, the Command x16 emulator 4bpp support did not work... however now it's been fixed...

Lets use it to create two 320x240 sixteen color (4bpp) layers and see what they can do!... we'll even 'upgrade' our hello world code to work in 4bpp!

x16_Multilayer.asm

4bpp Bitmap font

We're going to convert our old code, and make it 4bpp... because we've halved the number of bytes, our screen is now 160 bytes wide.

We need to alter the calculation code of our screen calculation code.

If we remove two of the rotate commands, we'll effectively halve the calculations from 320 bytes per X line to 160




We need to change the byte copying code... the 4bpp mode has 2 pixels per byte (with each pixel color defined by a nibble in the byte)... we need to shift 2 bits from the source font into our byte before storing it to the screen...

We also need to alter our 'newline' code, as the screen is 160 bytes wide, and our font is 4 bytes wide... so the calculation now adds 160-4 to move down a line.

2x 4bpp Layers

Just like last time we need to set the registers for Layer 1 with the registers at VRAM $040000

Last time we set the top 3 bits to 7 (BMP 8bpp)... this time we want to use mode 6 (BMP 4bpp)... we're going to set the screen up as 320x240 again, as there are now two pixels per byte, this time the screen will be 320x200/2=$9600 bytes in size.

We need to define an offset for the layer in VRAM with Registers $40004/5.... the first layer is at $00000 (like before)
Ok, on to the second layer... it's register format is identical... but the registers at $040010

Again we set up a layer in Mode 6... so this layer will also be 4bpp

We need to define an offset for the second layer in VRAM with Registers $40014/5.... we don't want it to overlap the last screen, so we'll put the second layer is at $0A000 (like before)
Due to the size of the screens, there isn't enough memory for two 320x240 bitmap screens with 256 colors, but 16 color layers are likely to be enough for any realistic needs!

Most systems from the 90's only had 16 colors or less anyway, so it's what a classic system had to work with.

Putting a bitmap on the screen

We're going to show a bitmap to the screen!

The 4bpp format is simple... 2 pixels per screen byte... the top nibble (%XXXX----) is the left pixel... the bottom nibble (%----XXXX) is the right pixel

Essentially, the x16 4bpp mode is the same format as most other 16 color systems like the MSX (but not the CPC Mode 0!)
We're going to load our address bitmap (in program code) into zero page entry z_hl... the 4bpp bitmap is 48x48

The VRAM destination will be the top left of screen Layer 2... as the screen starts at $0A000, this is the address we write to for the top left.
We need to copy all the data from our bitmap to the screen...

Because our screen is 4bpp (2 pixels per byte)... we only need to write 24 bytes per line...

We use a function IncHL to update the zero page z_hl pair...

When we get to the end of a line (when the X register reaches zero)... we do the next line... we need to add 160 to the VRAM position, but we need to subtract the 24 byte width
The Chibiko bitmap on Layer 2 has been drawn over the background text on Layer 1 !

We'll notice the background color 0 of the bitmap in layer 2 is transparent... we'll also notice the colors are wrong!

Changing the layer

Lets fix those colors... The layer uses only 16 colors of our 256 color palette... but we can alter WHICH 16 colors...

Rather than using colors 0-15 (&00-&0F) we can set it to use colors 16-31 (&10-&1F)
We do this with VRAM register $40017 - L2_BM_PAL_OFFS... In tile modes this is a scroll option (L2_HSCROLL_H) but in bitmap modes the register has the alternate purpose...

Effectively the bottom nibble of $40017 sets the top nibble of the color for Layer 2... (suffice to say there is a similar register for Layer 1 at $40007)
If we want, we can hide the layer, we just turn it off with the bottom bit of $40010
The 'Hscroll' option works as a Palette offset for bitmap modes... unfortunately it seems there's no hardware scroll option for the bitmap modes...

If we want to do scrolling, we're probably going to be best to use tilemap modes (we'll look at them soon)... otherwise we'd have to use software scroll.


X16:3 - Hardware Sprites on the Commander x16
The X16 is capable of good hardware sprites... with sizes from 8x8-64x64 - in 16 or 256 colors,with 128 sprites onscreen We've got a lot of capability!

Lets learn how to get some sprites onscreen!

x16_SpriteTest.asm

Setting Hardware sprites

Each of the 128 sprites are controlled by settings held in VRAM between $40800-$40FFF... each has 8 bytes...
The actual bitmap data of the sprites is held in normal VRAM - so we want to store it outside the visible screen...

Before we can actually use sprites, however, we need to turn them on, we do this with Bit 0 of $40020

Group Address range
Description
Screen Ram $00000-$1FFFF

Video RAM (also sprite bitmap data)
SprCom $40020 SPR_CTRL -------E E=Enable Sprites
SprCom $40021 SPR_COLLISION ----CCCC C=Collision Mask
SprCom $40022-$4002F

Sprite control registers
SprData $40800 Sprite 0: Address L LLLLLLLL L = Vram Address L
SprData $40801 Sprite 0: Address H M---HHHH H=Vram Address H / M= Mode (0=4bpp 1=8bpp)
SprData $40802 Sprite 0: Xpos L XXXXXXXX Xpos bits 0-7
SprData $40803 Sprite 0: Xpos H ------XX Xpos bits 8-9
SprData $40804 Sprite 0: Ypos L YYYYYYYY Ypos bits 0-7
SprData $40805 Sprite 0: Ypos H ------YY Ypos bits 8-9
SprData $40806 Sprite 0: Settings 1 CCCCZZVH C=Collision mask Z=Z depth V=Vflip H=Hflip
SprData $40807 Sprite 0: Settings 2 HHWWPPPP H=Height , W=Width (0/1/2/3 = 8/16/32/64 px) / P=Palette offset
SprData $40808-$40FFF
... Sprite data 1-127 (8 bytes per sprite)

Bitmap Data format

The Sprites can be in two formats:
in 8bpp (256 color ) mode, each byte is the color number
in 4bpp (16 color ) mode ,a byte covers two pixels... the High Nibble (%XXXX----) is the left pixel, the Low Nibble (%----XXXX) is the right pixel

Data in these formats can be exported from my AkuSprite Editor

Some useful new functions for VRAM

We're going to define a new macro called 'SetVeraAddr'... It will set the 3 bytes of the 20 bit address (and the autoinc parameter)... this will be a bit easier than our previous SetVeraHML command
We're going to define a new function 'VLDIR'... it uses z_BC and z_HL zeropage pairs and mimics the Z80 LDIR command.

this will load z_BC bytes from z_hl and write them all to VERA port v_d1 ($9f23)

We can use this to copy bitmaps  to VRAM

Coding Sprites

We're going to copy data from the label 'Sprite' to the VRAM...

We need to copy our sprite bitmap to an area outside of visible VRAM... I copied the sprite to $10000 - which is outside of the visible screen for a single 4bpp layer
Now we need to turn sprites on, we do this by writing a #1 to $40020
We're going to create a simple function to set a hardware sprite!

We'll use A to define the hardware sprite number, zeropage z_IX for the Xpos, z_IY for the Ypos, and z_HL defines the source Vram address (bitshifted 5 times to the right)

Each sprite has 8 bytes of data, so we multiply A by 8 (via bitshifting) and add $40800 (as this is the base of the sprite data)

We use SetVeraHML to set the VRAM address to AYX

We're ready to send the sprite settings!
Ok... we're ready to start sending the sprite settings...

First we send the address of the sprite in VRAM (RORed 5 times)... the top bit of the address has a different purpose, it defines if the sprite is 256 color or 16 color - 0=4bpp,1=8bpp

Next we send the Xpos and Ypos
We're going to set up some more settings, the Z Layer position of the sprite (3=Top)
We'll set the size of the sprite to 16x16.
Here we've shown a single sprite frame, but if we have multiple versions of the sprite at different addresses, we just need to alter the address to update the animation of the sprite...

Take a look at the example in the sources file to see more!


Using our Sprite routine

We can use this function to draw a sprite to the screen,

the example shown will show a 256 color sprite (due to the $8--- at the top of z_hl) from memory position $10100 (10100 shifted right 5 times= $808)

Hardware sprite 0 will be shown at (10,10)
The sprite will be shown to screen.
The example in this episode works fine for hardware sprites 0-15, but not 16+... it's not clear why!
Unfortunately, with the documentation limited, and the emulator in an early state, When something doesn't work it's not clear if it's an Emulator but, Unclear documentation, or a mistake by the author of these tutorials... If a solution becomes clear later, the code sample will be updated.


 

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!