Create Color Computer binaries from hex values
The December 1987 issue of Rainbow has a very interesting program from John Mosley that purports to be “a four-voice music and graphics program” that works with the CoCo 1 and 2. It consists of a short BASIC program to create the graphics, and a shorter BASIC program to allow the reader to type in the hexadecimal values for the music, one value at a time.
Screw up one number, and you have to start over, this time not screwing up elsewhere. Or you have to understand POKEing enough to rePOKE just the numbers you screwed up, and know where they are—probably by writing a BASIC program to display values in memory and ask you if they’re correct, one by one.
It seemed more reasonable to type all of the hexadecimal numbers into a text file, and then use a script to create the BIN file; if there’s an error, fix only that error in the text file instead of retyping everything.
The format of BIN files turns out to be fairly simple. From Walter Zydhek’s Disk BASIC Unravelled II, there is a preamble and a postamble, both five bytes long. The preamble contains the length of the data and the address the data should be loaded to:
Byte | Value | Purpose |
---|---|---|
0 | 00 | Preamble flag |
1-2 | XXXX | Length of data block |
3-4 | XXXX | Load address |
The postamble contains the execution (EXEC) address:
Byte | Value | Purpose |
---|---|---|
0 | FF | Postamble flag |
1-2 | 0000 | Dummy value |
3-4 | XXXX | Execution address |
The script accepts the following arguments:
load address | decimal or hex address for location of binary data in CoCo RAM |
---|---|
exec address | decimal or hex address for starting execution; defaults to load address |
filenames | file[s] to pull data from; data can also be piped |
--basic | the text is a BASIC file; pull hex values from DATA lines |
--columns <column count> | verify that each line contains a specific column count |
--help | print this text |
--quiet | do not output bin data |
--verbose | provide information about the binary program |
There’s some ambiguity in specifying the load and execution addresses. If the address is four characters long, the script assumes it is a hexadecimal number. I did this because my sense is that these numbers are usually provided as hexadecimal numbers. The script will provide a warning if it makes this assumption on a number that is all digits with no letters.
If you’re specifying a decimal number that happens to be four characters long, prepend the number with a zero to make it five characters long, to ensure that it is converted from decimal to hexadecimal.
The files consist of hexadecimal numbers. If you don’t specify a column count, the lines can each contain any number of numbers.
20 | 2 | 20 | E | 8E | 40 | 0 | EC | 84 | 44 | 54 | ED |
81 | 8C | 43 | FE | 2F | F5 | 86 | 3F | 1F | 8B | B6 | FF |
1 | 84 | F7 | B7 | FF | 1 | B6 | FF | 3 | 84 | F7 | B7 |
FF | 3 | 86 | 3C | B7 | FF | 23 | 34 | 1 | 1A | 50 | 8E |
44 | 0 | A6 | 80 | B7 | 3F | CD | EC | 81 | 97 | C0 | D7 |
C3 | EC | 81 | 97 | C6 | D7 | C9 | 20 | 16 | 81 | FE | 26 |
10 | EC | 84 | 97 | C0 | D7 | C3 | EC | 2 | 97 | C6 | D7 |
C9 | A6 | 4 | 97 | CD | 30 | 8 | A6 | 80 | 2B | E6 | 27 |
The file can also contain comments, denoted by pound symbols. The script skips over any line beginning with “#”.
You can specify files by filenames (and it will accept multiple filenames, using them in order), or by piping data to the script.
If a number of columns is provided, the script verifies that each line (except the final line) contains that many hexadecimal numbers. If any line except the final line contains a different number of items, the script dies with an error message. The final line is allowed to contain a different number, because there often aren’t enough numbers to fill out the final line.
The script outputs to STDOUT, so to save it to a file use the redirect symbol “>” to redirect to the desired filename.
Here’s how I created the BIN file for Mosley’s program using this script, and copied it to a TEST.DSK file using Trash Tools:
- cocobin ml\ song.txt 16128 > "ml song.bin"
- decb kill test.dsk,ML\ SONG.BIN
- decb copy -b -2 ml\ song.bin test.dsk,ML\ SONG.BIN
You could also send it to XRoar (which is what I used to create the video) using xroar ml\ song.bin.
Mosley’s ml song.bin loads to and executes from 16128. That’s 3F00 in hexadecimal:
Load address: | 3F00 (16128) |
EXEC address: | 3F00 (16128) |
Length: | 102C (4140) |
If you have the hex values in DATA lines in a BASIC program, you can use that BASIC file as your source; add the --basic argument to the script and it will ignore non-DATA lines, and will assume that all DATA elements are hex values.
Here’s the full script:
[toggle code]
- #! /usr/bin/perl
- # Convert text file of HEX values to Color Computer .BIN file
- # Jerry Stratton astoundingscripts.com
- # BIN file format from Walter K. Zydhek's Disk BASIC Unravelled II
- # Preamble:
- # BYTE 0 00 Preamble Flag
- # BYTEs 1-2 Length of data block
- # Bytes 3-4 Load address
- # Postamble:
- # Byte 0 FF Postamble flag
- # Byte 1-2 0000
- # Byte 3-4 EXEC address
-
while ($option = shift) {
-
if (-f $option) {
- $files[$#files+1] = $option;
-
} elsif ($option =~ /^[0-9a-f]{4}/i || $option =~ /^[0-9]+$/) {
- print STDERR "Assuming $option is a hexadecimal number\n" if length($option) == 4 && $option =~ /^[0-9]+$/;
- $option = sprintf("%04X", $option) if length($option) != 4 && $option =~ /^[0-9]+$/;
-
if (!$loadAddress) {
- $loadAddress = $option;
-
} elsif (!$execAddress) {
- $execAddress = $option;
-
} else {
- die("Unknown address $option");
- }
-
} elsif ($option eq '--basic') {
- $basicCode = 1;
-
} elsif ($option eq '--columns') {
- die('--columns requires a column count') if $#ARGV < 0 || $ARGV[0] !~ /[1-9][0-9]*/;
- $columnCount = shift;
-
} elsif ($option eq '--help') {
- help();
-
} elsif ($option eq '--quiet') {
- $quiet = 1;
-
} elsif ($option eq '--verbose') {
- $verbose = 1;
-
} else {
- die("Unknown option $option");
- }
-
if (-f $option) {
- }
- die("Load and Exec addresses required.") if !$loadAddress;
- $execAddress = $loadAddress if !$execAddress;
- @ARGV = @files;
-
while (<>) {
- chomp;
- $lineCount = 0 if $ARGV ne $file;
- $file = $ARGV;
- $lineCount++;
- next if /^#/;
- next if /^$/;
-
if ($basicCode) {
- next if !/^[1-9][0-9]* DATA/;
- s/^[1-9][0-9]* DATA *//;
- }
- #verify that the *previous* line contained the correct amount of columns
- #this ensures that the final line does not get checked, as it often contains fewer columns
- die($#hexes+1, " columns in line $lineCount ($file) with text: $_") if $#hexes >= 0 && $columnCount && $#hexes != $columnCount-1;
- @hexes = split(/[ ,\t]+/);
-
foreach $hex (@hexes) {
- next if $hex =~ /^$/;
- die("Invalid hexadecimal: $hex in line $lineCount ($file) with text: $_") if $hex !~ /^[0-9a-f]{1,2}$/i;
- $code .= dehexByte($hex);
- }
- }
- $length = sprintf("%04X", length($code));
-
if ($verbose) {
- print STDERR "Load address: $loadAddress (${\hex($loadAddress)})\n";
- print STDERR "EXEC address: $execAddress (${\hex($execAddress)})\n";
- print STDERR "Length: $length (${\hex($length)})\n";
- }
- $length = twoByteDehexer($length);
- $load = twoByteDehexer($loadAddress);
- $exec = twoByteDehexer($execAddress);
- $preamble = chr(0) . $length . $load;
- $postamble = chr(255) . chr(0) . chr(0) . $exec;
- $code = $preamble . $code . $postamble;
- print $code if !$quiet;
-
sub twoByteDehexer {
- my $hex = shift;
- my $hex1 = dehexByte(substr($hex, 0, 2));
- my $hex2 = dehexByte(substr($hex, 2, 2));
- return "$hex1$hex2";
- }
-
sub dehexByte {
- my $hex = shift;
- $hex = "0$hex" if length($hex) < 2;
- my $char1 = substr($hex, 0, 1);
- my $char2 = substr($hex, 1, 1);
- $char1 = dehexChar($char1);
- $char2 = dehexChar($char2);
- $dec = $char1*16+$char2;
- return chr($dec);
- }
-
sub dehexChar {
- my $char = shift;
- $char = uc($char);
-
if ($char == 0 && $char ne '0') {
- $char = ord($char) - ord('A') + 10;
- }
- return $char;
- }
-
sub help {
- print "$0 <load address> [exec address] [filenames] [--columns <column count>] [--help] [--verbose]\n";
- print "\tload address: decimal or hex address for location of binary data in CoCo RAM\n";
- print "\texec address: decimal or hex address for starting execution; defaults to load address\n";
- print "\tfilenames: file[s] to pull data from; data can also be piped\n";
- print "\t--basic: data file is a text BASIC file; pull hex values from DATA lines\n";
- print "\t--columns <column count>: verify that each line contains a specific column count\n";
- print "\t--help: print this text\n";
- print "\t--quiet: do not output bin data\n";
- print "\t--verbose: provide information about the binary program\n";
- exit();
- }
Currently I’ve used this on all of two files—Mosley’s and a program for adding 8k to BASIC’s available RAM1. If I find more uses for it, it is likely to change considerably to meet whatever new needs come up. If it turns out to be too annoying to have to preface four-digit decimal numbers with a zero, I might require that hexadecimal numbers be prefaced with an ‘H’ or ‘x’, for example. It’s also likely that some binaries were provided as decimal instead of hexadecimal numbers.
In response to TRS-80 Color Computer Programming Tools: The TRS-80 Color Computer was a fascinating implementation of the 6809 computer chip, and was, from the Color Computer 1 through 3, possibly the longest-running of the old-school personal computers.
- February 13, 2021: Color Computer binaries from decimal values
-
At the end of my post on the first version of cocobin, I wrote:
It’s also likely that some binaries were provided as decimal instead of hexadecimal numbers.
And only a few weeks later, here I am. I found a really nice Galaxian/Space Invaders-style game by Rodger Smith in the February 1985 Hot CoCo.1
He used decimal numbers for his DATA statements, so I added that feature to cocobin. If a file or BASIC program contains decimal rather than hexadecimal numbers, add the option
--decimal
to thecocobin
command line.Also as expected, adding this feature also highlighted another common custom of the era: the DATA items often included a marker to denote the end of the data. Most commonly, as I recall, this was the word “END”, or (for machine code) a negative 1. Smith used the number 999. So I’ve added the ability to recognize “END”, “999”, or “-1” at the end of a line of DATA in a BASIC program. If the program sees any of those key words at the end of a DATA line, it assumes that that is the end of the data to be read.
Either of those keywords not at the end of a DATA line will still be seen as an error, since none of them represent valid POKEable numbers.
The script now accepts the following arguments:
load address decimal or hex address for location of binary data in CoCo RAM exec address decimal or hex address for starting execution; defaults to load address filenames file[s] to pull data from; data can also be piped --basic the text is a BASIC file; pull hex values from DATA lines --columns <column count> verify that each line contains a specific column count --decimal the numbers are decimal numbers, not hexadecimal --help print this text --quiet do not output bin data --verbose provide information about the binary program Also, while this is not a change, I did finally verify that the script works when used with multiple files. Charles Husak’s “The Little Runner” from the March 1984 Rainbow uses three BASIC programs to POKE the binary into memory. This command line worked to create a working binary from those three files:
- cocobin --basic 13000 RUNNER*.BAS > RUNNER.BIN
The latter is Ray Gauvreau’s “A Bigger Byte for Basic” from the January 1984 Rainbow, which is a bit tricky since it’s designed specifically for non-Disk BASIC. This program is why I added the option to pull hex values from BASIC DATA lines.
↑
- Disk BASIC Unravelled II: Walter K. Zydhek at Internet Archive (ebook)
- “Disk BASIC Unravelled will provide the reader with a detailed and fully commented assembly listing of the Disk Operating System (DOS) of Radio Shack’s COLOR BASIC.”
- Merry CoCo Christmas at Mimsy@YouTube
- “From the December 1987 Rainbow, John Mosley’s ‘Do You Hear What I Hear’ features ‘four voice music’.”
- ToolShed (os9 & decb)
- “ToolShed brings you the ease and speed of developing these programs to your modern personal computer running Windows, Mac OS X or Linux. The tools in the shed consist of a relocatable macro assembler/linker and intermediate code analyzer, a stand-alone assembler, an OS-9/Disk BASIC file manager and more.”
- XRoar—Dragon & CoCo emulator
- “XRoar is a Dragon emulator for Linux, Unix, Mac OS X and Windows. Due to hardware similarities, XRoar also emulates the Tandy Colour Computer (CoCo) models 1 & 2. More features.”