SkillAgentSearch skills...

Goodasm

A portable assembler for Z80, 8080, Gameboy, 6805, 8051 and others.

Install / Use

/learn @travisgoodspeed/Goodasm
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

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

Demo 3/1 from 2600 Games

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.

GameBoy Example

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

NipperTool dumping a Dish Network ROM3 Card

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
View on GitHub
GitHub Stars47
CategoryDevelopment
Updated19d ago
Forks10

Languages

C++

Security Score

75/100

Audited on Mar 18, 2026

No findings