CoCo Assembler and File Utility
Table of Contents
What is it?
This project is an assembler for the Tandy Color Computer 1, 2 and 3 written in Python 3.6. It is intended to be statement compatible with any code written for the EDTASM+ assembler. The assembler is capable of taking EDTASM+ assembly language code and translating it into machine code for the Color Computer 1, 2, and 3. Current support is for 6809 CPU instructions, but future enhancements will add 6309 instructions.
This project also includes a general purpose file utility, used mainly for manipulating CAS
, DSK
, and WAV
files.
Requirements
In order to run the assembler, you will need Python 3.6 or greater. If you prefer to clone the repository, you will need Git (if you don't want to install Git, then check the Releases section for the newest release of the package that you can download).
License
This project makes use of an MIT style license. Please see the file called LICENSE for more information.
Installing
To install the source files, download the latest release from the Releases section of the repository and unzip the contents in a directory of your choice. Or, clone the repository in the directory of your choice with:
git clone https://github.com/craigthomas/CoCoAssembler.git
Next, you will need to install the required packages for the file:
pip install -r requirements.txt
Assembler
The assembler is contained in a file called assmbler.py
and can be invoked with:
python assembler.py
In general, the assembler recognizes EDTASM+ mnemonics, along with a few other somewhat standardized mnemonics to make program compilation easier. By default, the assembler assumes it is assembling statements in 6809 machine code. Future releases will include a 6309 extension.
Assembler Usage
To run the assembler:
python assembler.py input_file
This will assemble the instructions found in file input_file
and will generate the associated Color Computer machine instructions in binary format. You will need to save the assembled contents to a file to be useful. There are several switches that are available:
--print
- prints out the assembled statements--symbols
- prints out the symbol table--bin_file
- save assembled contents to a binary file--cas_file
- save assembled contents to a cassette file--dsk_file
- save assembled contents to a virtual disk file--name
- saves the program with the specified name on a cassette or virtual disk file
Input File Format
The input file needs to follow the format below:
LABEL MNEMONIC OPERANDS COMMENT
Where:
LABEL
is a 10 character label for the statementMNEMONIC
is a 6809 operation mnemonic from the Mnemonic Table belowOPERANDS
are registers, values, expressions, or labels as described in the Operands sectionCOMMENT
is a 40 character comment describing the statement (must have a;
preceding it)
An example file:
; Print HELLO WORLD on the screen
NAM HELLO ; Name of the program
CHROUT EQU $A30A ; Location of CHROUT routine
POLCAT EQU $A000 ; Location of POLCAT routine
ORG $0E00 ; Originate at $0E00
START JSR $A928 ; Clear the screen
LDX #MESSAGE ; Load X index with start of message
PRINT LDA ,X+ ; Load next character of message
CMPA #0 ; Check for null terminator
BEQ FINISH ; Done printing, wait for keypress
JSR CHROUT ; Print out the character
BRA PRINT ; Print next char
MESSAGE FCC "HELLO WORLD"
FDB $0 ; Null terminator
FINISH JSR [POLCAT] ; Read keyboard
BEQ FINISH ; No key pressed, wait for keypress
JMP $A027 ; Restart BASIC
END START
Print Symbol Table
To print the symbol table that is generated during assembly, use the --symbols
switch:
python assembler.py test.asm --symbols
Which will have the following output:
-- Symbol Table --
$A30A CHROUT
$A000 POLCAT
$0E00 START
$0E06 PRINT
$0E11 MESSAGE
$0E1E FINISH
The first column of output is the hex value of the symbol, the second columns is the symbol name itself.
Print Assembled Statements
To print out the assembled version of the program, use the --print
switch:
python assembler.py test.asm --print
Which will have the following output:
-- Assembled Statements --
$0000 NAM HELLO ; Name of the program
$0000 CHROUT EQU $A30A ; Location of CHROUT routine
$0000 POLCAT EQU $A000 ; Location of POLCAT routine
$0E00 ORG $0E00 ; Originate at $0E00
$0E00 BDA928 START JSR $A928 ; Clear the screen
$0E03 8E0E11 LDX #MESSAGE ; Load X index with start of message
$0E06 A680 PRINT LDA ,X+ ; Load next character of message
$0E08 8100 CMPA #0 ; Check for null terminator
$0E0A 2712 BEQ FINISH ; Done printing, wait for keypress
$0E0C BDA30A JSR CHROUT ; Print out the character
$0E0F 20F5 BRA PRINT ; Print next char
$0E11 48454C4C4F MESSAGE FCC "HELLO WORLD" ;
$0E1C 0000 FDB $0 ; Null terminator
$0E1E AD9FA000 FINISH JSR [POLCAT] ; Read keyboard
$0E22 27FA BEQ FINISH ; No key pressed, wait for keypress
$0E24 7EA027 JMP $A027 ; Restart BASIC
$0E27 END START ;
A single line of output is composed of 6 columns:
Line: $0E00 BDA928 START JSR $A928 ; Clear the screen
----- ------ ----- --- ----- ------------------
Column: 1 2 3 4 5 6
The columns are as follows:
- The offset in hex where the statement occurs (
$0E00
). - The machine code generated and truncated to 10 hex characters (
BDA928
). - The user-supplied label for the statement (
START
). - The instruction mnemonic (
JSR
). - Operands that the instruction processes (
$A928
). - The comment string (
Clear the screen
).
Save to Binary File
To save the assembled contents to a binary file, use the --bin_file
switch:
python assembler.py test.asm --bin_file test.bin
The assembled program will be saved to the file test.bin
. Note that this file may not be useful on its own, as it does not have any meta information about where the file should be loaded in memory (pseudo operations ORG
and NAM
will not have any effect on the assembled file).
NOTE: If the file test.bin
exists, it will be erased and overwritten.
Save to Cassette File
To save the assembled contents to a cassette file, use the --cas_file
switch:
python assembler.py test.asm --cas_file test.cas
This will assemble the program and save it to a cassette file called test.cas
. The source code must include the NAM
mnemonic to name the program (e.g. NAM myprog
), or the --name
switch must be used on the command line (e.g. --name myprog
). The program name on the cassette file will be myprog
.
NOTE: if the file test.cas
exists, assembly will stop and the file will not be overwritten. If you wish to add the program to test.cas
, you must specify the --append
flag during compilation:
python assembler.py test.asm --cas_file test.cas --append
This will add the file to the test.cas
file as an additional binary within the cassette. To load from the cassette file using BASIC's CLOADM
command would be done as follows
CLOADM"MYPROG"
Mnemonic Table
Below are the mnemonics that are accepted by the assembler (these mnemonics are compatible with EDTASM+ assembler mnemonics). For the mnemonics below, special symbols are:
A
- 8-bit accumulator register.B
- 8-bit accumulator register.CC
- 8-bit condition code register.D
- 16-bit accumulator register comprised of A and B, with A being high byte, and B being low byte.M
- a memory location with a value between 0 and 65535.S
- 16-bit system stack register.U
- 16-bit user stack register.X
- 16-bit index register.Y
- 16-bit index register.
Mnemonics
Mnemonic | Description | Example |
---|---|---|
ABX |
Adds contents of B to value in register X and stores in X . |
ABX |
ADCA |
Adds contents of A , memory value M , and carry bit and stores in A . |
ADCA $FFEE |
ADCB |
Adds contents of B , memory value M , and carry bit and stores in B . |
ADCB $FFEF |
ADDA |
Adds contents of A with memory value M and stores in A . |
ADDA #$03 |
ADDB |
Adds contents of B with memory value M and stores in B . |
ADDB #$90 |
ADDD |
Adds contents of D (A:B ) with memory value M:M+1 and stores in D . |
ADDD #$2000 |
ANDA |
Performs a logical AND on A with memory value M and stores in A . |
ANDA #$05 |
ANDB |
Performs a logical AND on B with memory value M and stores in B . |
ANDB $FFEE |
ANDCC |
Performs a logical AND on CC with immediate value M and stores in CC . |
ANDCC #01 |
ASLA |
Shifts bits in A left (0 placed in bit 0, bit 7 goes to carry in CC ). |
ASLA |
ASLB |
Shifts bits in B left (0 placed in bit 0, bit 7 goes to carry in CC ). |
ASLB |
ASL |
Shifts bits in M left (0 placed in bit 0, bit 7 goes to carry in CC ). |
ASL $0E00 |
ASRA |
Shifts bits in A right (bit 0 goes to carry in CC , bit 7 remains same). |
ASRA |
ASRB |
Shifts bits in B right (bit 0 goes to carry in CC , bit 7 remains same). |
ASRB |
ASR |
Shifts bits in M right (bit 0 goes to carry in CC , bit 7 remains same). |
ASR $0E00 |
Pseudo Operations
Mnemonic | Description | Example |
---|---|---|
FCB |
Defines a single byte constant value. | FCB $1C |
FCC |
Defines a string constant value enclosed in a matching pair of delimiters. | FCC "hello" |
FDB |
Defines a two byte constant value. | FDB $2000 |
END |
Defines the end of the program. | END |
EQU |
Defines a symbol with a set value. | SCRMEM EQU $1000 |
INCLUDE |
Includes another assembly source file at this location. | INCLUDE globals.asm |
NAM |
Sets the name for the program when assembled to disk or cassette. | NAM myprog |
ORG |
Defines where in memory the program should originate at. | ORG $0E00 |
RMB |
Defines a block of n bytes initialized to a zero value. | RMB $8 |
SETDP |
Sets the direct page value for the assembler (see notes below). | SETDP $0E00 |
Notes
SETDP
- this mnemonic is used for memory and instruction optimization by the assembler. For example, ifSETDP $0E00
is set, any machine instructions that use$0EXX
as a memory location will be assembled using direct page addressing. Instructions such asJMP $0E8F
will becomeJMP <$8F
. The programmer is responsible for loading the direct page register manually - this mnemonic does not output opcodes that change the direct page register.
Operands
(Under construction)
Addressing Modes
(Under construction)
File Utility
The file utility included with the assembler package provides a method for manipulating and extracting information from image files (e.g. CAS
or DSK
files).
Listing Files
To list the files contained within a cassette or disk image, use the --list
switch:
python file_util.py --list test.cas
The utility will print a list of all the files contained within the image, along with associated meta-data:
-- File #1 --
Filename: HELLO
File Type: Object
Data Type: Binary
Gap Status: No Gaps
Load Addr: $0E00
Exec Addr: $0E00
Data Len: 39 bytes
-- File #2 --
Filename: WORLD
File Type: Object
Data Type: Binary
Gap Status: No Gaps
Load Addr: $0F00
Exec Addr: $0F00
Data Len: 73 bytes
-- File #3 --
Filename: ANOTHER
File Type: Object
Data Type: Binary
Gap Status: No Gaps
Load Addr: $0C00
Exec Addr: $0C00
Data Len: 24 bytes
Extracting to Binary File
To extract the files from a disk or cassette image, and save each one as a binary, use the --to_bin
switch:
python file_util.py --to_bin test.cas
To command will list the files being extracted and their status:
-- File #1 [HELLO] --
Saved as HELLO.bin
-- File #2 [WORLD] --
Saved as WORLD.bin
-- File #3 [ANOTHER] --
Saved as ANOTHER.bin
Note that no meta-data is saved with the extraction (meaning that load addresses, and execute addresses are not saved in the resulting .bin
files). If you only wish to extract a specific subset of files, you can provide a space-separated, case-insensitive list of filenames to extract with the --files
switch:
python file_util.py --to_bin test.cas --files hello another
Which will result in:
-- File #1 [HELLO] --
Saved as HELLO.bin
-- File #3 [ANOTHER] --
Saved as ANOTHER.bin
Extracting to Cassette File
To extract the files from a disk image, and save each one as a cassette file, use the --to_cas
switch:
python file_util.py --to_cas test.dsk
To command will list the files being extracted and their status:
-- File #1 [HELLO] --
Saved as HELLO.cas
-- File #2 [WORLD] --
Saved as WORLD.cas
-- File #3 [ANOTHER] --
Saved as ANOTHER.cas
If you only wish to extract a specific subset of files, you can provide a space-separated, case-insensitive list of filenames to extract with the --files
switch:
python file_util.py --to_cas test.dsk --files hello another
Which will result in:
-- File #1 [HELLO] --
Saved as HELLO.cas
-- File #3 [ANOTHER] --
Saved as ANOTHER.cas
Common Examples
Below are a collection of common examples with their command-line usage.
Appending to a Cassette
To assemble a program called myprog.asm
and add it to an existing cassette file:
python assembler.py myprog.asm --cas_file my_cassette.cas --append
Listing Files in an Image
To list the files contained within an container file (such as CAS
or DSK
file):
python file_util.py --list my_cassette.cas
This will print out a list of all the file contents on the cassette image my_cassette.cas
. Note that BIN
or binary only file contents only have a single program with no meta-information stored in them. As such, no information will be available for binary file types.
Extracting Binary Files from Cassette Images
To extract all the binary files from a cassette image:
python file_util.py --to_bin my_cassette.cas
Will extract all of the files in the image file my_cassette.bin
to separate files ending in a .bin
extension. No meta-information about the files will be saved in .bin
format.
Extracting Binary Files from Disk Images
To extract all the binary files from a disk image:
python file_util.py --to_bin my_disk.dsk
Will extract all of the files in the image file my_disk.dsk
to separate files ending in a .bin
extension. No meta-information about the files will be saved in .bin
format.