The SYM-1 is a 6502-based single-board computer produced by Synertek Systems Corp in the mid 1970’s. I’ve had one floating around in a box for many, many years, and after a recent foray into the world of 6502 assembly language programming I decided to pull it out, dust it off, and see if it still works.

The board I have has a whopping 8KB of memory, and in addition to the standard SUPERMON monitor it has the expansion ROMs for the Synertek BASIC interpreter (yet another Microsoft BASIC) and RAE (the “Resident Assembler Editor”). One interacts with the board either through the onboard hex keypad and six-digit display, or via a serial connection at 4800bps (or lower).

[If you’re interested in Microsoft BASIC, the mist64/msbasic repository on GitHub is a trove of information, containing the source for multiple versions of Microsoft BASIC including the Synertek version.]

Fiddling around with the BASIC interpreter and the onboard assembler was fun, but I wanted to use a real editor for writing source files, assemble them on my Linux system, and then transfer the compiled binary to the SYM-1. The first two tasks are easy; there are lots of editors and there are a variety of 6502 assemblers that will run under Linux. I’m partial to ca65, part of the cc65 project (which is an incredible project that implements a C compiler that cross-compiles C for 6502 processors). But what’s the best way to get compiled code over to the SYM-1?

Symtool

That’s where symtool comes in. Symtool runs on your host and talks to the SUPERMON monitor on the SYM-1 over a serial connection. It allows you to view registers, dump and load memory, fill memory, and execute code.

Configuration

Symtool needs to know to what serial device your SYM-1 is attached. You can specify this using the -d <device> command line option, but this quickly gets old. To save typing, you can instead set the SYMTOOL_DEVICE environment variable:

$ export SYMTOOL_DEVICE=/dev/ttyUSB0
$ symtool load ...
$ symtool dump ...

The baud rate defaults to 4800bps. If for some reason you want to use a slower speed (maybe you’d like to relive the good old days of 300bps modems), you can use the -s command line option or the SYMTOOL_SPEED environment variable.

Loading code into memory

After compiling your code (I’ve included the examples from the SYM-1 Technical Notes in the repository), use the load command to load the code into the memory of the SYM-1:

$ make -C asm
[...]
$ symtool -v load 0x200 asm/countdown.bin
INFO:symtool.symtool:using port /dev/ttyUSB0, speed 4800
INFO:symtool.symtool:connecting to sym1...
INFO:symtool.symtool:connected
INFO:symtool.symtool:loading 214 bytes of data at $200

(Note the -v on the command line there; without that, symtool won’t produce any output unless there’s an error.)

[A note on compiling code: the build logic in the asm/ directory is configured to load code at address 0x200. If you want to load code at a different address, you will need to add the appropriate --start-addr option to LD65FLAGS when building, or modify the linker configuration in sym1.cfg.]

Examining memory

The above command loads the code into memory but doesn’t execute it. We can use the dump command to examine memory. By default, dump produces binary output. We can use that to extract code from the SYM-1 ROM or to verify that the code we just loaded was transferred correctly:

$ symtool dump 0x200 $(wc -c < asm/countdown.bin) -o check.bin
$ sha1sum check.bin asm/countdown.bin
5851c40bed8cc8b2a132163234b68a7fc0e434c0  check.bin
5851c40bed8cc8b2a132163234b68a7fc0e434c0  asm/countdown.bin

We can also produce a hexdump:

$ symtool dump 0x200 $(wc -c < asm/countdown.bin) -h
00000000: 20 86 8B A9 20 85 03 A9  55 8D 7E A6 A9 02 8D 7F   ... ...U.~.....
00000010: A6 A9 40 8D 0B AC A9 4E  8D 06 AC A9 C0 8D 0E AC  ..@....N........
00000020: A9 00 85 02 A9 20 8D 05  AC 18 58 A9 00 8D 40 A6  ..... ....X...@.
00000030: 8D 41 A6 8D 44 A6 8D 45  A6 A5 04 29 0F 20 73 02  .A..D..E...). s.
00000040: 8D 43 A6 A5 04 4A 4A 4A  4A 20 73 02 8D 42 A6 20  .C...JJJJ s..B.
00000050: 06 89 4C 2B 02 48 8A 48  98 48 AD 0D AC 8D 0D AC  ..L+.H.H.H......
00000060: E6 02 A5 02 C9 05 F0 02  50 66 A9 00 85 02 20 78  ........Pf.... x
00000070: 02 50 5D AA BD 29 8C 60  18 A5 04 69 01 18 B8 85  .P]..).`...i....
00000080: 04 C9 FF F0 01 60 A9 7C  8D 41 A6 A9 79 8D 42 A6  .....`.|.A..y.B.
00000090: 8D 43 A6 A9 73 8D 44 A6  A9 00 85 04 20 72 89 20  .C..s.D..... r.
000000A0: 06 89 20 06 89 20 06 89  20 06 89 20 06 89 20 06  .. .. .. .. .. .
000000B0: 89 C6 03 20 06 89 20 06  89 20 06 89 20 06 89 20  ... .. .. .. ..
000000C0: 06 89 20 06 89 A5 03 C9  00 D0 D1 A9 20 85 03 60  .. ......... ..`
000000D0: 68 A8 68 AA 68 40                                 h.h.h@

Or a disassembly:

$ symtool dump 0x200 $(wc -c < asm/countdown.bin) -d
$0200   20 86 8b    JSR     $8B86
$0203   a9 20       LDA     #$20
$0205   85 03       STA     $03
$0207   a9 55       LDA     #$55
$0209   8d 7e a6    STA     $A67E
$020c   a9 02       LDA     #$02
$020e   8d 7f a6    STA     $A67F
$0211   a9 40       LDA     #$40
$0213   8d 0b ac    STA     $AC0B
$0216   a9 4e       LDA     #$4E
$0218   8d 06 ac    STA     $AC06
$021b   a9 c0       LDA     #$C0
$021d   8d 0e ac    STA     $AC0E
$0220   a9 00       LDA     #$00
$0222   85 02       STA     $02
$0224   a9 20       LDA     #$20
$0226   8d 05 ac    STA     $AC05
$0229   18          CLC
$022a   58          CLI
$022b   a9 00       LDA     #$00
$022d   8d 40 a6    STA     $A640
$0230   8d 41 a6    STA     $A641
$0233   8d 44 a6    STA     $A644
$0236   8d 45 a6    STA     $A645
$0239   a5 04       LDA     $04
$023b   29 0f       AND     #$0F
$023d   20 73 02    JSR     $0273
$0240   8d 43 a6    STA     $A643
$0243   a5 04       LDA     $04
$0245   4a          LSR
$0246   4a          LSR
$0247   4a          LSR
$0248   4a          LSR
$0249   20 73 02    JSR     $0273
$024c   8d 42 a6    STA     $A642
$024f   20 06 89    JSR     $8906
$0252   4c 2b 02    JMP     $022B
$0255   48          PHA
$0256   8a          TXA
$0257   48          PHA
$0258   98          TYA
$0259   48          PHA
$025a   ad 0d ac    LDA     $AC0D
$025d   8d 0d ac    STA     $AC0D
$0260   e6 02       INC     $02
$0262   a5 02       LDA     $02
$0264   c9 05       CMP     #$05
$0266   f0 02       BEQ     $02
$0268   50 66       BVC     $66
$026a   a9 00       LDA     #$00
$026c   85 02       STA     $02
$026e   20 78 02    JSR     $0278
$0271   50 5d       BVC     $5D
$0273   aa          TAX
$0274   bd 29 8c    LDA     $8C29,X
$0277   60          RTS
$0278   18          CLC
$0279   a5 04       LDA     $04
$027b   69 01       ADC     #$01
$027d   18          CLC
$027e   b8          CLV
$027f   85 04       STA     $04
$0281   c9 ff       CMP     #$FF
$0283   f0 01       BEQ     $01
$0285   60          RTS
$0286   a9 7c       LDA     #$7C
$0288   8d 41 a6    STA     $A641
$028b   a9 79       LDA     #$79
$028d   8d 42 a6    STA     $A642
$0290   8d 43 a6    STA     $A643
$0293   a9 73       LDA     #$73
$0295   8d 44 a6    STA     $A644
$0298   a9 00       LDA     #$00
$029a   85 04       STA     $04
$029c   20 72 89    JSR     $8972
$029f   20 06 89    JSR     $8906
$02a2   20 06 89    JSR     $8906
$02a5   20 06 89    JSR     $8906
$02a8   20 06 89    JSR     $8906
$02ab   20 06 89    JSR     $8906
$02ae   20 06 89    JSR     $8906
$02b1   c6 03       DEC     $03
$02b3   20 06 89    JSR     $8906
$02b6   20 06 89    JSR     $8906
$02b9   20 06 89    JSR     $8906
$02bc   20 06 89    JSR     $8906
$02bf   20 06 89    JSR     $8906
$02c2   20 06 89    JSR     $8906
$02c5   a5 03       LDA     $03
$02c7   c9 00       CMP     #$00
$02c9   d0 d1       bNE     $D1
$02cb   a9 20       LDA     #$20
$02cd   85 03       STA     $03
$02cf   60          RTS
$02d0   68          PLA
$02d1   a8          TAY
$02d2   68          PLA
$02d3   aa          TAX
$02d4   68          PLA
$02d5   40          RTI

Executing code

There are two ways to run your code using symtool. If you provide the -g option to the load command, symtool will execute your code as soon as the load has finished:

$ symtool load -g 0x200 asm/countdown.bin

Alternatively, you can use the go command to run code that has already been loaded onto the SYM-1:

$ symtool go 0x200

Examining registers

The registers command allows you to examine the contents of the 6502 registers:

$ symtool registers
s ff (11111111)
f b1 (10110001) +carry -zero -intr -dec -oflow +neg
a 80 (10000000)
x 00 (00000000)
y 50 (01010000)
p b0ac (1011000010101100)

Filling memory

If you want to clear a block of memory, you can use the fill command. For example, to wipe out the code we loaded in the earlier example:

$ symtool fill 0x200 0 $(wc -c < asm/countdown.bin)
$ symtool dump -h 0x200 $(wc -c < asm/countdown.bin)
00000000: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[...]

Notes on the code

The symtool repository includes both unit and functional tests. The functional tests require an actual SYM-1 to be attached to your system (with the device name in the SYMTOOL_DEVICE environment variable). The unit tests will run anywhere.

Wrapping up

No lie, this is a pretty niche project. I’m not sure how many people out there own a SYM-1 these days, but this has been fun to work with and if maybe one other person finds it useful, I would consider that a success :).

See Also