Skip to content
David Banks edited this page Jun 6, 2021 · 7 revisions

The documentation for this project is at an early stage, so please bear with me. If you have any specific questions, please do ask them in the thread over on Stardot.

Overview

Logic analyzers are widely available, yet they are seldom the tool of choice when it comes to debugging 6502-based computer systems. There are a number of reasons for this:

  1. To follow what the 6502 is doing, it's typically necessary to monitor the data bus (8 signals), the address bus (16 signals) plus a set of miscellaneous control lines (7 signals). This means a 32-channel logic analyzer must be used. These are out of the price range of many hobbyists, and time consuming to hook up.

  2. It can be quite involved to work backwards from the values of the externally visible bus signals to piece together the instruction stream being executed.

  3. Often the point of interest (a system malfunction), can be buried in many millions of correctly executed instructions, and conventional logic analyzers interfaces become unwieldy with this amount of data.

This project enables a cheap (<£10) 16-channel USB logic analyzer to be used as an effective diagnostic tool for 6502-based computer systems, overcoming each of the above issues:

  1. Minimally just Phi2 (the system clock) and D7..D0 (the data bus) need to be connected. Adding just a couple more control lines (Sync, RnW) can improve the results in certain cases, but these are optional.

  2. The values on the data bus are used to reconstruct the full 6502 machine state, including the internal processor registers, the external memory state, and the stream of instructions being executed.

  3. Configurable triggers allow the user to set the conditions required for decoding to commence.

Background

The motivation for this project came from the author's experiences trying to use a variety of "classic" HP logic analyzers to fix broken 1980's vintage Acorn microcomputers.

The HP 1650A is quite limited by modern standards, and only has a capture depth of 1024 samples. Optional software, called by HP an Inverse Assembler, does allow some understanding of the instruction stream. But the user interface is very poor.

The HP 16702B is a much more capable device (being based on a 150MHz PA-RISC Processor and running HP-UX internally). Yet the Inverse Assembler also extremely difficult to use effectively.

I then stumbled across Sigrok, an Open-Source signal analysis software suite that works with many different USB logic analyzers. It allows custom protocol decoders to be developed in python, and ships with one for the Z80.

Inspired by this, I started work on the 6502 Protocol Decoder for Sigrok. At the same time, I started blogging my experiences in a series of posts on stardot: Open Source Logic Analyzer Experiments

It quickly became apparent that Sigrok and it's python protocol decoders were painfully slow for this type of application. Decoding 1-seconds worth of 6502 bus data was taking 1-2 minutes (on my 2009 vintage Linux workstation).

Hence the current version of this tool is a standalone C program, which runs ~100x faster then the python code in Sigrok.

Blog Posts (on Stardot)

I've written up the adventures as a set of posts over on stardot, entitled Open Source Logic Analyzer Experiments:

Example of the current decoder (taken from Part 6)

$ decode6502 -h -s --sync= <data.bin >data.txt

results in:

???? :          : RESET !!      A=?? X=?? Y=?? SP=?? N=? V=? D=? I=1 Z=? C=?
D9CD : A9 40    : LDA #40       A=40 X=?? Y=?? SP=?? N=0 V=? D=? I=1 Z=0 C=?
D9CF : 8D 00 0D : STA 0D00      A=40 X=?? Y=?? SP=?? N=0 V=? D=? I=1 Z=0 C=?
D9D2 : 78       : SEI           A=40 X=?? Y=?? SP=?? N=0 V=? D=? I=1 Z=0 C=?
D9D3 : D8       : CLD           A=40 X=?? Y=?? SP=?? N=0 V=? D=0 I=1 Z=0 C=?
D9D4 : A2 FF    : LDX #FF       A=40 X=FF Y=?? SP=?? N=1 V=? D=0 I=1 Z=0 C=?
D9D6 : 9A       : TXS           A=40 X=FF Y=?? SP=FF N=1 V=? D=0 I=1 Z=0 C=?
D9D7 : AD 4E FE : LDA FE4E      A=80 X=FF Y=?? SP=FF N=1 V=? D=0 I=1 Z=0 C=?
D9DA : 0A       : ASL A         A=00 X=FF Y=?? SP=FF N=0 V=? D=0 I=1 Z=1 C=1
D9DB : 48       : PHA           A=00 X=FF Y=?? SP=FE N=0 V=? D=0 I=1 Z=1 C=1
D9DC : F0 09    : BEQ D9E7      A=00 X=FF Y=?? SP=FE N=0 V=? D=0 I=1 Z=1 C=1
D9E7 : A2 04    : LDX #04       A=00 X=04 Y=?? SP=FE N=0 V=? D=0 I=1 Z=0 C=1
D9E9 : 86 01    : STX 01        A=00 X=04 Y=?? SP=FE N=0 V=? D=0 I=1 Z=0 C=1
D9EB : 85 00    : STA 00        A=00 X=04 Y=?? SP=FE N=0 V=? D=0 I=1 Z=0 C=1
D9ED : A8       : TAY           A=00 X=04 Y=00 SP=FE N=0 V=? D=0 I=1 Z=1 C=1
D9EE : 91 00    : STA (00),Y    A=00 X=04 Y=00 SP=FE N=0 V=? D=0 I=1 Z=1 C=1
D9F0 : C5 01    : CMP 01        A=00 X=04 Y=00 SP=FE N=1 V=? D=0 I=1 Z=0 C=0
D9F2 : F0 09    : BEQ D9FD      A=00 X=04 Y=00 SP=FE N=1 V=? D=0 I=1 Z=0 C=0
D9F4 : C8       : INY           A=00 X=04 Y=01 SP=FE N=0 V=? D=0 I=1 Z=0 C=0
D9F5 : D0 F7    : BNE D9EE      A=00 X=04 Y=01 SP=FE N=0 V=? D=0 I=1 Z=0 C=0

Command Line Options

There are a growing number of command line options. For now, please refer to the built-in help.

dmb@quadhog:~/atom/6502Decoder$ ./decode6502 --help
Usage: decode6502 [OPTION...] [FILENAME]

Decoder for 6502/65C02/65C816 logic analyzer capture files.

FILENAME must be a binary capture file containing:
- 16 bit samples (of the data bus and control signals), or
-  8-bit samples (of the data bus), if the --byte option is present.

If FILENAME is omitted, stdin is read instead.

The default sample bit assignments for the 6502/65C02 signals are:
 - data: bit  0 (assumes 8 consecutive bits)
 -  rnw: bit  8
 - sync: bit  9
 -  rdy: bit 10
 -  rst: bit 14
 - phi2: bit 15

The default sample bit assignments for the 65C816 signals are:
 - data: bit  0 (assumes 8 consecutive bits)
 -  rnw: bit  8
 -  vpa: bit  9
 -  rdy: bit 10
 -  vda: bit 11
 -    e: bit 12
 -  rst: bit 14
 - phi2: bit 15

To specify that an input is unconnected, include the option with an empty
BITNUM. e.g. --sync=

If phi2 is not connected the capture file should contain one sample per
falling edge of phi2.

If rdy is not connected a value of '1' is assumed.

If sync (or vda/vpa) is not connected a heuristic based decoder is used.
This works well, but can take several instructions to lock onto the
instruction stream. Use of sync (or vda/vpa) is recommended.

If RST is not connected, an alternative is to specify the reset vector:
 - D9CD (D9 is the high byte, CD is the low byte)
 - A9D9CD (optionally, also specify the first opcode, LDA # in this case)

If --debug=1 is specified, each instruction is preceeded by it's sample
values.

The --mem= option controls the memory access logging and modelling. The value
is three hex nibbles: WRM, where W controls write logging, R controls read
logging, and M controls modelling.
Each of the three nibbles has the same semantics:
 - bit 3 applies to stack accesses
 - bit 2 applies to data accesses
 - bit 1 applies to pointer accesses
 - bit 0 applies to instruction accesses
Examples:
 --mem=00F models (and verifies) all accesses, but with minimal extra logging
 --mem=F0F would additional log all writes


 General options:
  -b, --byte                 Enable byte-wide sample mode
      --bbctube              BBC tube protocol decoding
  -c, --cpu=CPU              Sets CPU type (6502, 65c02, r65c02, 65c816)
  -d, --debug=LEVEL          Sets the debug level (0 or 1)
  -m, --machine=MACHINE      Enable machine (beeb,elk,master) defaults
      --mem[=HEX]            Memory modelling (see above)
  -p, --profile[=PARAMS]     Profile code execution
      --skip[=HEX]           Skip the first n samples
  -t, --trigger=ADDRESS      Trigger on address
      --vecrst[=HEX]         Reset vector, optionally preceeded by the first
                             opcode (e.g. A9D9CD)

 Output options:
  -a, --address              Show address of instruction
  -f, --bbcfwa               Show BBC floating-point work areas
  -h, --hex                  Show hex bytes of instruction
  -i, --instruction          Show instruction disassembly
  -q, --quiet                Set all the output options to off
  -r, --showromno            Show BBC rom no for address 8000..BFFF
  -s, --state                Show register/flag state
  -y, --cycles               Show number of bus cycles

 Signal defintion options:
      --data=BITNUM          Bit number for data (default  0)
      --e[=BITNUM]           Bit number for e    (default 12) (65C816)
      --phi2[=BITNUM]        Bit number for phi2 (default 15)
      --rdy[=BITNUM]         Bit number for rdy  (default 10)
      --rnw[=BITNUM]         Bit number for rnw  (default  8)
      --rst[=BITNUM]         Bit number for rst  (default 14)
      --sync[=BITNUM]        Bit number for sync (default  9) (6502/65C02)
      --vda[=BITNUM]         Bit number for vda  (default 11) (65C816)
      --vpa[=BITNUM]         Bit number for vpa  (default  9) (65C816)

 Additional 6502/65C02 options:
      --sp[=HEX]             Initial value of the Stack Pointer register
  -u, --undocumented         Enable undocumented opcodes

 Additional 65C816 options:
      --db[=HEX]             Initial value of the Data Bank register
      --dp[=HEX]             Initial value of the Direct Page register
      --emul[=HEX]           Initial value of the E flag
      --ms[=HEX]             Initial value of the M flag
      --pb[=HEX]             Initial value of the Program Bank register
      --sp[=HEX]             Initial value of the Stack Pointer register
      --xs[=HEX]             Initial value of the X flag

  -?, --help                 Give this help list
      --usage                Give a short usage message
  -V, --version              Print program version

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.
Clone this wiki locally