There are many MOS 6502 cross-assemblers available. Here’s a new one. Or actually a very old one. “Macross”, a very powerful 6502 macro assembler, which was used to create Habitat, Maniac Mansion and Zak McKracken, was developed between 1984 and 1987 at Lucasfilm Ltd. and is now Open Source (MIT license):

https://github.com/Museum-of-Art-and-Digital-Entertainment/macross

Some History

Starting in 1984, a team at Lucasfilm Ltd. was developing one of the first online role-playing games, originally called “Microcosm”, which was released as “Habitat” in 1986 and later renamed to “Club Caribe”. The client ran on a Commodore 64, which conntected to the central server through the Quantum Link network.

The client software was developed on a 68K-based Sun workstation running the SunOS variant of Unix using cross-development tools developed by Chip Morningstar (who was also the Habitat lead): The “Macross” assembler and the “Slinky” linker. They were used on every 6502 (Atari 400/800, Commodore 64, and Apple II) game produced at Lucasfilm Games, from 1984 up until those machines ceased to be relevant to the games market*.

In 2014, The Museum of Art and Digital Entertainment got a hold of

the source of the original development tools (Macross/Slinky)

the source of the C64 client

the source of the server (written in PL/I)

lots of documentation and development logs

which originated from an archive created in 1987 in the context of the technology transfer to Fujitsu, which bought all Habitat assets.

Since Macross and Slinky were written for Unix, it was easy to get them compiling with modern compilers (K&R syntax notwithstanding) and running on a modern Unix system. At the time of writing, the code in the official repository has been fixed to compile with clang on OS X. Further fixes and cleanups are very welcome.

Compiling Macross

Enter “make” both in the toplevel directory and in the “slinky” directory, then copy “macross” and “slinky” into your path. There are man files in the “doc” directory that you may want to install as well.

Writing Code

The syntax of Macross source files is very different from modern 6502 cross assembler, and more similar to Commodore’s own “A65” assembler. Here is a small “Hello World” for the C64:

define strout = 0xab1e hello: lda #/text ldy #?text jmp strout text: byte "HELLO WORLD!", 0

As you can see, hex values have to be specified in C notation (binary is prefixed with “0b”), and the operators to extract the low and high bytes of a 16 bit value are “/” and “?”, respectively.

Compile and link the source file like this:

macross -c -o hello.o hello.m slinky -e -o hello.bin -n -m hello.sym -l 0xc000 hello.o dd if=hello.bin bs=1 skip=2 count=2 of=hello.prg dd if=hello.bin bs=1 skip=6 >> hello.prg

The “dd” lines convert Slinky’s output, which is a “standard a65-style object file” (which has a header of FF FF, followed by the start address, followed by the end address) into a C64 .PRG file that is only prefixed by the start address.

Here is a slightly more complex example:

define bsout = 0xffd2 hello: ldx #0 do { lda x[text] cmp #'A' if (geq) { tay iny tya } inx jsr bsout } while (!zero) rts text: byte "HELLO WORLD!", 0

Macross supports C-style if/else, while and do/while, as well as do/until, where the condition can be one of:

zero/equal

carry

lt/leq/gt/geq

slt/sleq/sgt/sgeq

positive/plus/negative/minus

overflow

…as well as their negated versions.

Also note that the “absolute, x-indexed” addressing mode has a different syntax than commonly used.

Macros

Macross has a very powerfull macro language. Here is an example:

org 0xc000 function makeFirstByte(operand) { mif (isImmediateMode(operand)) { freturn(/operand) } melse { freturn(operand) } } function makeSecondByte(operand) { mif (isImmediateMode(operand)) { freturn(?operand) } melse { freturn(operand + 1) } } macro movew src, dst { lda makeFirstByte(src) sta makeFirstByte(dst) lda makeSecondByte(src) sta makeSecondByte(dst) } macro hook_vector index, new, dst { ldx #index * 2 movew x[0x0300], dst movew #new, x[0x0300] } define VECTOR_INDEX_IRQ = 10 hook_vector VECTOR_INDEX_IRQ, irq, return + 1 rts irq: inc 0xd020 return: jmp 0xffff

The “hook_vector” line will emit the following assembly code:

ldx #$14 lda $0300,x sta $C01D lda $0301,x sta $C01E lda #$19 sta $0300,x lda #$C0 sta $0301,x

(The example is a little contrived, since adding the index could have been done at assembly time, but the example nicely demonstrates that macros can preserve addressing modes.)

The file doc/macros.itr contains many useful macros. they are documented in doc/genmacros.itr.

Full Documentation

The complete documentation of Macross is available in the file doc/writeup_6502.itr in the repository. It is in troff format and can viewed like this:

nroff -ms doc/writeup_6502.itr

Future

Macross is a very different approach to 6502 development, and with the source available, I think it’s a viable project that should be continued.

I will happily accept pull requests for compile fixes (GCC, VS, …), cleanups (C99, converting docs from troff to markdown, …) and features (BIN and PRG output, support for more a modern notation, PETSCII, …).