65c02 Assembly programming for the Commander x16

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

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.

Download the source code (x16_HelloWorld.asm)

Watch this tutorial on Youtube!


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!


Download the source code (x16_HelloWorld.asm)

Watch this tutorial on Youtube!


 

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!