SkillAgentSearch skills...

6809

A 6809 assembler, simulator and debugger written in rust.

Install / Use

/learn @gorsat/6809
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

6809 Assembler, Simulator, Debugger (in rust)

The venerable Motorola 6809 was the first microprocessor I knew. I have great fondness for it and when I was looking for a project through which to learn Rust, I figured a 6809 assembler might be just the ticket. But then I wanted to be able to run the output on something, so I decided to write a simple simulator, too. And then I needed to debug both the assembler and the simulator so I bolted on a simple debugger as well.

I ended up with this little 6809 swiss army knife and the world got yet another piece of software focused on a processor from 40+ years ago. It's not optimal, or cycle-accurate, or a good example of Rust code. But it will build and run some substantial assembly language programs (like Microsoft Extended Basic), and run them much faster than they ever ran on the real processor. It might be useful for someone doing some retro work and it's definitely entertaining for those who want to relive the magic of early 1980s computing.

Note: If you're looking for a TRS-80 Color Computer emulator then check out gorsat/coco. Using the coco emulator, you can load the EDTASM+ cartridge and directly edit/build/run 6809 assembler in the old coco console. It's arguably not as developer friendly as this 6809 simulator, but it's lots of fun :grin:

Getting Started

This program runs on Mac, Windows and Linux (only tested on Ubuntu). To sanity check it, you can run a quick test:

cargo test

...and after several lines of output it should say something like:

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s

To assemble and run a program:

cargo run -- -r /path/to/program.asm

...or if you've already built the binary then just...

6809 -r /path/to/program.asm

Options

Help for command line options is available using -h or --help. There are a lot of options and I won't cover all of them here. Hopefully most of them are sufficiently self explanatory.

MC6809 assembler, simulator and debugger written in Rust.

Usage: 6809 [OPTIONS] <FILE>

Arguments:
  <FILE>  Assembly (.asm, .s) or Hex (.hex) file to assemble/run/debug

Options:
      --acia-disable
          Disable ACIA emulation
      --acia-addr <ACIA_ADDR>
          Address at which to map the ACIA (hex ok with '0x') [default: 65488]
      --acia-port <ACIA_PORT>
          TCP port on which to expose ACIA [default: 6809]
      --acia-debug
          Print ACIA debug information
      --acia-case
          Swap the case of alpha ASCII characters received via ACIA (a->A;A->a)
  -b, --break-start
          Break into the debugger before running the program (only if debugger enabled)
  -c, --code-only
          Remove blank and comment-only lines from program listing
  -d, --debug
          Run the program with debugger enabled (if both -r and -d are specified then -d wins)
      --history <HISTORY>
          The number of instructions to keep in the execution history when debugging [default: 200]
  -l, --list
          If there is a program listing then dump it to stdout
      --lbr-disable
          Disable automatic branch->long_branch conversion
  -n, --no-auto-sym
          No automatic loading of symbols
  -p, --pemdas
          Automatically evaluate expressions using PEMDAS rather than left-to-right
      --perf
          Display perf data (only interesting for longer-running programs)
      --ram-top <RAM_TOP>
          Set the top RAM address [default: 32767]
      --reset-vector <RESET_VECTOR>
          Override the reset vector
  -r, --run
          Run the program with debugger disabled and evaluate any test criteria
  -t, --trace
          Trace each machine instruction as it is executed
  -v, --verbose
          Enable verbose output
  -w, --write-files
          Write output files after assembly (.lst, .sym, .hex)
  -h, --help
          Print help information
  -V, --version
          Print version information

NOTE: The program can load/run/debug a hex file (in I8HEX format) rather than an assembly language file but it requires the file to have the .hex extension. You still have to specify that you want to --run the program, i.e., 6809 -r my_program.hex

Assembler

The assembler supports a number of features including:

  • simple macros - See below. Macros are expanded prior to the build process and parameters are supported.
  • test criteria - See below. Test criteria are evaluated after the completion of a --run.
  • direct mode addressing - you can force direct mode by prepending a '<' to an 8-bit address
  • current location reference - you can use '*' to refer to the "current location" in the program, so that, for example, the statement jmp * is an infinite loop.
  • automatic branch extension - if you use a Bxx instruction but the destination is too far away then it will be automatically converted to an LBxx instruction (and you can turn this off with the --lbr-disable option)
  • multiple literal types - in addition to decimal you can use hex values ($ff) and binary (%0101) and character literals ('Z) in operands
  • left-to-right and pemdas - Assembly language programs (like Microsoft Extended Basic) relied on the fact that expressions in operands would be evaluated left-to-right rather than following the regular PEMDAS rule. Thus, the assembler uses left-to-right as the default order of operations. In other words, the expression 2+8/2 evaluates to 5 rather than 6. However, you can just specify -p or --pemdas to change this. Also, whether in PEMDAS mode or not, the assembler supports parentheses so that 2+(8/2) will always evaluate to 6.

Macros

There are a few examples of assembly language programs in the test directory that show several features of the assembler. The program mbadd.asm includes the following trivial macro definition:

   .macro my_bne
       bne @0
   .endm

Once defined, this macro can be used anywhere you'd use a regular instruction and it will be expanded with parameters, so that, for example, the statement

     my_bne LOOP

would be replaced with

     bne LOOP

prior to the build process. Macros are not limited in terms of number of lines or parameters. I will caveat this by saying that I have not got around to adding signficant macro tests to the test suite.

Including Files

A simple .include <asm_file_path> statement enables code reuse and basic modularity by literally inserting the contents of the referenced .asm file within the current file. So, for example, if this is file1.asm:

    lda #1
    .include include/file2.asm
    swi

...and ./include/file2.asm is the following:

; this is file2
    ldb #2

...loading file1.asm results in the following combined listing:

    lda #1
; this is file2
    ldb #2
    swi

...which is then assembled. You can include at multiple levels of depth, but not recursively (so, for example, file2.asm could include a file3.asm, but it would cause an error if file2.asm or file3.asm included file1.asm).

Directives

The following assembler directives are suppported:

  • EQU <br>Syntax: Label EQU <expression> <br>Example: ANSWER EQU 21*2 <br>Sets Label equal to a constant. The constant can be provided as an expression.
  • FCB <br>Syntax: [Label] FCB <expression> [,<expression>...] <br>Example: hex_ans fcb $42 <br>Sets a byte (or sequence of bytes) in the program binary equal to the evaluated expression(s). Each evaluated expression must fit into 8 bits. The list of expressions must not contain spaces. The optional Label will be equal to the address where the first byte is stored in the binary.
  • FDB <br>Syntax: [Label] FDB <expression> [,<expression>...] <br>Example: Magic FDB 378+ANSWER,$face,$45,'E <br>Sets a 16-bit word (or sequence of words) in the program binary equal to the evaluated expression(s). Each evaluated expression must fit into 16 bits. The list of expressions must not contain spaces. The optional Label will be equal to the address where the first word is stored in the binary.
  • FCC <br>Syntax: [Label] FCC <delim><ascii_string><delim> <br>Example: Hello FCC "Hello, world!" <br>Stores an ascii string as bytes within the program binary. The deliminators can be any character but they must match one another. Any characters following the second deliminator are ignored. The optional Label will be equal to the address where the first byte of the string is stored in the binary.
  • ORG <br>Syntax: [Label] ORG <expression> <br>Example: Start org $1000 <br>Sets the current address within the program binary. The next instruction or data directive (e.g. FCB) will begin at the address defined by the expression. The optional Label will be equal to this address.
  • RMB <br>Syntax:[Label] RMB <expression> <br>Example:Table rmb 13*14 <br>Reserves a block of bytes whose size is given by expression. The current program address is increased by this number of bytes, thus the next instruction or data directive will begin at the address just after the reserved bytes. The optional Label will be equal to the address of the first byte in the reserved block.
  • SETDP <br>Syntax: SETDP [<expression>] <br>Example: setdp Table/256 <br>Tells the assembler to assume that the DP register will be set to the value given by expression at runtime. This allows the assembler to optimize for direct mode addressing. By default, DP is assumed to be 0. You can turn off direct mode optimization by using SETDP without an expression. If the expression evaluates to an 8-bit number then DP is assumed to be equal to that number. If expression evaluates to a 16-bit number then the LSB must be equal to 0 and DP is assumed t
View on GitHub
GitHub Stars14
CategoryDevelopment
Updated14d ago
Forks0

Languages

Assembly

Security Score

95/100

Audited on Mar 21, 2026

No findings