Commander CX16 Wiki

A file format for saving binary programs, data, and graphic information.

The X16 file format specifies the load address and memory type for data in the file


This is a description of the draft “X16” binary file format for graphics, music, and programs with embedded resources. 

The X16 format will contain:

  • Main memory data
  • Banked (Upper) memory data
  • VERA register data
  • Bitmap binary data
  • Tile layout binary data
  • Pallette data
  • Data for external devices, loaded through I/O port. 

This format is intended for non-relocatable binary data and is not a container format for text, music, video, or other data streams that have a self-identified structure.

File Layout[]


$16 FF

The first two bytes of a Commodore binary specify the loading address. Any load address in the $FFxx range are impossible to load on a Commander X16, since ROM occupies the top 8K of memory. So any address with $FF as the second byte is a unique format.

Following the two byte header is a series of blocks. Blocks continue until the end of the file.

$01 00 00 File Format Version. 

Specification version. Major, Minor, revision numbers. The value should be exactly as shown above. This is represented as 3 octets in reverse order: ie: $01 02 03 becomes 3.2.1

File readers are expected to handle a specific version of the spec. If this value is not recognized by the reader, fail with “Unexpected format version code. Expected 0.0.1, got n.n.n”, where n.n.n are the actual octets in the file. Optionally, your reader may handle older versions at your discretion. 

$01 00 00 File version

This is application-dependent. Use this as you wish. Recommend parsing this and enforcing your own file versioning for your internal data structures. 

“32 byte string” Comment

The comment text may be up to 32 bytes. The comment may be any text up to 32 bytes in length. The content of this field should not affect your program in any way. This is intended to be a free comment field, not metadata for your program

Block Layout []

Position Name Value Meaning
0 TYPE $00 Configuration. Consume this directly; do not load into RAM. Application dependent.
$01 Main Memory: This will be loaded into main memory (the lower 38K or the banked memory starting at $A000)
$02 VERA graphic data. This will be loaded into VERA at the BANK and ADDRESS listed below
$03 Register data: writes to a small set of addresses starting at ADDRESS.

RANGE determines how many addresses are written to.

01-02 ADDRESS The start address in little-endian order.
03 BANK First memory bank to select while loading data. This is required for VERA ($02) and when loading data in $A000 or above.
LOOP For register data, this is the number of bytes over which the load loops. The write address should start at ADDRESS, then advance this many times, then reset to ADDRESS. So when data is written to 1 port number (the most common situation), this should be 0.
04-05 LENGTH The length of this data block's payload. Read the next LENGTH bytes.
06-LENGTH+5 DATA Data to be read. This section should be LENGTH bytes long
LENGTH+6 END $E0B1 End Of Block. Get it?

The data blocks may repeat as many times as necessary. Data in each block should be contiguous in memory, and empty spans of more than a few bytes should be split up into separate blocks.

Also, it is advisable to split each 8K bank of upper memory into a separate block. This allows the loader to relocate blocks, if necessary.

Use block type of 0 for configuration data, metadata, or any data that is not directly loaded to memory. For example, you can include your own version control information or internal file type specifiers. In this block type, the bank and address values will be ignored, but must be included. LENGTH must be correct and reflect the length of the data in the block.

When writing to I/O registers, use ADDRESS for the register address. LOOP should be 00 to write to a single address. If you set it to 01 or higher, the loader will write to ADDRESS, then ADDRESS+1, and loop when ADDRESS+n bytes have been written. So a value of 3 will cause data to be looped across 4 bytes, then loop back around again to the first address. This optional implementation is intended for output to serial ports or other hardware devices, and further custom handling may be needed by the loader. Use Block type 0 to  

Custom data will NOT be parsed or converted. The block types are for your program’s information, and you will need to process these data blocks as appropriate. The reference loader will have a section for implementing custom loader routines. In the absence of a custom routine, data will be read into RAM at ADDRESS and BANK.

Use cases[]

One use case will be to load graphics into VERA. To do so, you should dump each VERA bank into separate blocks. Start by writing out the bank data at $F:0000 for 32 bytes, then write out the layer registers as needed. 

Finally, write your bitmap data, tile data, and palette data as separate blocks. 

$02 00 00 0F 20 00 The first 6 bytes sets the system up to write to VERA at bank 15, address 0 and write 32 bytes[]

$01 80 80 0E 00 80 00 E0 The next 32 bytes set up the video registers for $20 00 00 00 00 00 00 00 text mode (ie: 16 color 1bpp 8x8 tile mode.) $00 00 00 00 00 00 00 00 $00 00 00 00 00 00 00 00

$F0 0F End of block

$02 00 00 00 00 3C Write to address $0:0000 in VERA, for 15,360 bytes.[]

$08 61 05 61 0C 61 0C 61 “Hello world.” $0F 61 20 61 17 61 0F 61 $12 61 0C 61 04 61 20 61 $20 61 20 61 20 61 20 61

$F0 0F End of block

You might also use this to load data into banked (upper) memory. 

$01 00 A0 03 00 20 Loads data into main memory. Start writing at $A000 Write to bank 3 Write $2000 (8192) bytes

You should use a separate block for each 8K bank, to make loading simple. However, your loader should support wrapping at the bank boundary. Ie: when the write pointer reaches $C000, change it to $A000 and increment the bank register. 


If you have gaps in your data of more than 8 bytes, it may be more efficient to use separate blocks than to store 8 or more zeroes in your data. 

We do not recommend using compression in your data stream, except as above to skip non-defined regions in your data. This is because the Commander X16 CPU is likely to be slower than the storage media. This means that any decompression algorithm will actually take longer to unpack the data than it would to simply read from disk. 

The free text header should never affect the behavior of your program. You may read, parse, and display that data (and this is encouraged in your file picker), but you should not expect any specific value to be there, and your program should not fail to load the file based on that data. This field is entirely for the user’s benefit.

Reference Implementations[]

Reference loaders and savers will be provided, in BASIC and Assembly code. These will be provided for use in any application and will be public domain.

In general, expect the program flow to work like this:

Read the header and confirm the format version number. 

Read the file version number and JSR to user code to pass the file version to the user program.

Read the block type. 

For block types 01-03, load data into RAM. For all other block types, JSR to user code with the block type in .A. Block header data will be in a specific location in RAM or on the stack.

After read is complete, set .A with a status result and RTS to user code. 


This document is © 2019 Tom Wilson. 

Final, non-draft versions are free to use with attribution, and will be noted as “Final” in version control at the bottom of this page. You may copy and distribute this document verbatim or modify it as needed to match your program’s implementation. Please note in Version Control if you have deviated  from the baseline. 

Draft versions of this document should not be distributed except for the purposes of developing the specification itself. 

This specification is free for public use. You may use it for any purpose without restriction. You may use this specification, modify it, and distribute this specification with your programs. No person may attempt to restrict this specification or derivative works through the use of Copyright, Patent, or Trademarks. 

No warranty of any kind is provided. Use at your own risk.

Version Control[]

Draft 0.1[]

  • Added file format version, file version, and free text in the header.
  • Changed block byte 0 to TYPE and changed type codes (values went up by 1, added new type 0.)
  • Added Copyright and license information.