Goodasm
A portable assembler for Z80, 8080, Gameboy, 6805, 8051 and others.
Install / Use
/learn @travisgoodspeed/GoodasmREADME
Howdy y'all,
These days, the world has many excellent disassemblers, but when writing assembly in a weird languages, many of us just fall back to handwriting bytes in Nasm or painfully adjusting to yet another macro assembler. GoodASM is my attempt to fix that.
This assembler is very easy to retarget by extending C++ class and calling a few functions. You pretty much just define the endianness and byte size, then copy the instruction definitions almost verbatim from the programmer's guide.
Defining a new target is completely symmetric. While assembly is the primary purpose of this tool, a disassembler is also produced. An example for each instruction automatically becomes a unit test, insuring that you don't accidentally fat-finger a bitfield or two.
When writing assembly, particularly shellcode, you often have
questions that a calculator should answer. What are all the
instruction that begin with ro in this language? What are all of
the potential forms of the jmp instruction? Does ror a assemble
to ASCII? An interactive REPL mode answers these in a jiffy, with tab
completion.
There is no license, but you're welcome to use this software anyways. If you like this program, please buy my book on Microcontroller Exploits for yourself or for a clever student.
--Travis
Status
Some critical features remain to be written before a public release, but enough features are supported for 6502 and the GameBoy to write real programs. See the Issue Tracker for known bugs and missing features.
A language is alpha if it is relatively unused, beta if successful
test programs have been written for it, and stable if it has been
used for real projects and compared against competing assemblers for
accuracy. broken and partial languages are not yet complete, and you should
check the issue tracker for their status.
Source code and binaries were publicly released at DistrictCon on Feb 21, 2025.
Building
For GUI development, install the Qt Dev
Kit and then open
CMakefile.txt in Qt Creator. On Windows, you must also install the
Git client; Github Desktop is not
enough on its own.
To build in Linux, first install qt6-declarative-dev, qml6-module-\*, git
and cmake, then run the following:
git clone https://github.com/travisgoodspeed/goodasm
cd goodasm
mkdir build
cd build
cmake ..
make -j 8 clean all
The preferred executable is goodasm. The GUI for iOS and Android is
more of a fun toy than a tool.
Examples

tests/6502/nanochess contains translations of the first few
chapters of Programming Games for Atari
2600
by Oscar Toledo G. Hashes are compared to make sure that our
assembler produces the same binaries as Oscar's, and pull requests are
waiting if you convert any of the remaining demos from that book into
this assembler's style.

tests/sm83/hello.asm is a simple Hello World for the SM83
architecture of the Nintendo Game Boy.

tests/6805/nipperpatch.asm is the 6805 shellcode from my
NipperTool exploit
for the Nagra ROM3 cards that Dish Network used twenty-five years ago.
In NipperTool, this code is exported as Go source code with a map of
symbol names, so that the tool can patch the target address being
dumped by the shellcode.
Using the Disassembler
Running goodasm --help will show the command line arguments,
supported languages and other features.
% goodasm
Usage: goodasm [options] input
GoodASM is an easily retargetable assembler for CISC microcontrollers.
Options:
-h, --help Displays help on commandline options.
--help-all Displays help, including generic Qt options.
-v, --version Displays version information.
-V, --verbose Talk too much.
--cheat Print a cheat sheet of mnemonics.
--opcodetable Print a table of opcodes.
-t, --test Selftest the selected language.
--fuzz Fuzz test to find crashes.
-g, --grade Is this binary in the chosen language?
-i, --identify In what language is this binary?
-d, --dis Disassemble a binary input.
--base <base> Base address of an input binary.
-o, --export <file> Output filename.
-S, --symboltable <file> Export symbol table
--repl Interactive shell.
--wait Wait after cleanup.
-L, --list Print a listing to stdout.
-H, --hex Print hex of output to stdout.
-N, --nasm Output is legal in NASM.
-F, --goodasm Output is legal in GoodASM.
-C Output is legal in C.
-G, --golang Output is legal in Golang.
-Y, --yara-x Output is legal in Yara-X.
-M, --markdown Output is legal in Markdown.
-b, --bytes List bytes in output.
-B, --bits List bits in output.
-A, --auto Comment with cheat sheet when available.
-a, --adr List addresses in output.
--8086 Intel 8086 (broken)
--6502 MOS 6502 (stable)
--6805 68HC05 (stable)
--fcard 68HC05SC21 DSS P1 F-Card (stable)
--sm83, --gb Sharp SM83 / Gameboy (stable)
--z80 Zilog Z80 (stable)
--tlcs47 Toshiba TLCS47 (alpha)
--8051 Intel 8051 (alpha)
--ucom43 Sharp UCOM43 (alpha)
--s2000, --emz1001 AMI S2000 / Iskra EMZ1001 (alpha)
--pic16c5x PIC16C5x (12-bit) (alpha)
--marc4 Atmel MARC4 (alpha)
--chip8 Chip-8 (broken)
Arguments:
input ROM image to decode or source to assemble.
Let's begin with the guesses test file for AMI's S2000 architecture,
an assembly language I expect you've never heard of. Its biggest
claim to fame is that it was also called EMZ1001, Yugoslavia's only
locally manufactured microcontroller.
By default, GoodASM has no language defined, so it will just output
db directives.
% goodasm -da guesses
0x00: db 0x00
0x01: db 0x01
0x02: db 0x02
0x03: db 0x03
0x04: db 0x6f
0x05: db 0xcf
We can get a more useful listing by specifying the language with
--s2000. Of the single letter parameters, d disassembles, b
prints the bytes within the disassembly, a includes the address of
each instruction, and A auto-comments with the help entry of each
instruction.
% goodasm -dbaA --s2000 guesses
0000: 00 nop ; No operation.
0001: 01 brk ; Break.
0002: 02 rt ; Return from subroutine.
0003: 03 rts ; Return from subroutine and skip next instruction.
0004: 6f pp #0x000f ; Prepare page or bank register.
0005: cf jmp #0x000f ; Jump to immediate in prepared page and bank.
You can also use the interactive mode to type in bytes for disassembly. This is useful when writing shellcode by hand or working out errors in a damaged disassembly.
carbon% goodasm -dba --s2000 --repl
goodasm> 00 01 02 03 6f cf
0000: 00 nop
0001: 01 brk
0002: 02 rt
0003: 03 rts
0004: 6f pp #0x000f
0005: cf jmp #0x000f
goodasm> 6fcf
0000: 6f pp #0x000f
0001: cf jmp #0x000f
goodasm>
Using the Assembler
The assembler functions from the exact same definitions as the disassembler, so there is no language that works with one but not the other.
Use the -H parameter to dump the output as text or -o as a binary
file. -L prints an instruction listing, matching source code to the
binary. The same language flags work as in the disassembler, but you
can also use the .lang directive to specify it within your source
file.
% cat undefined.asm
.lang 6502
nop ; Do nothing.
brk ; 1-byte in documentation,
nop ; but we follow with padding.
% goodasm -H undefined.asm -o undefined.bin
ea 00 ea
% goodasm -Lba undefined.asm
0000: ea nop ; Do nothing.
0001: 00 brk ; 1-byte in docs.
0002: ea nop ; but we follow with padding.
Just like in disassembly, you can use the interactive mode to try out instructions. Tab completion will give you verb names and parameters.
% goodasm --6502
goodasm> st<TAB>
sta stx sty
goodasm> stx <TAB>
stx @0xdead
stx @0x35
stx @0x35, y
goodasm> stx @0xdead
[0] 8e ad de
goodasm>
Because every assembly language is unique, and it's a devil to remember all the forms of a new one, a handy cheat sheet from the instruction text cases will remind you of the available instructions.
% goodasm --6502 --cheat
adc #0xff ;; Add to A with Carry. (immediate)
adc @0xdead ;; Add to A with Carry. (absolute)
adc @0xdead, x ;; Add to A with Carry. (X-indexed absolute)
adc @0xdead, y ;; Add to A with Carry. (Y-indexed absolute)
adc @0x35 ;; Add to A with Carry. (zero-page)
adc @0x35, x ;; Add to A with Carry. (X-indexed zero-page)
adc (@0x35, x) ;; Add to A with Carry. (X-Indexed Zero
