mirror of
https://github.com/zellyn/go6502.git
synced 2025-01-15 21:31:50 +00:00
working on disasm
This commit is contained in:
parent
a9f4d9b8ff
commit
d504143800
118
asm/disasm.go
118
asm/disasm.go
@ -4,58 +4,92 @@ Package asm provides routines for decompiling 6502 assembly language.
|
|||||||
package asm
|
package asm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/zellyn/go6502/opcodes"
|
"github.com/zellyn/go6502/opcodes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Symbols are symbol tables used to convert addresses to names
|
||||||
|
type Symbols map[int]string
|
||||||
|
|
||||||
|
// name returns a symbol for an address. If lookback is 1 or greater,
|
||||||
|
// it can return strings like "FOO+1" for the addresses succeeding
|
||||||
|
// defined symbols.
|
||||||
|
func (s Symbols) name(addr int, lookback int) string {
|
||||||
|
if n, ok := s[addr]; ok {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
for i := 1; i <= lookback; i++ {
|
||||||
|
if n, ok := s[addr-i]; ok {
|
||||||
|
return fmt.Sprintf("%s+%d", n, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// bytesString takes three bytes and a length, returning the formatted
|
// bytesString takes three bytes and a length, returning the formatted
|
||||||
// hex bytes for an instrction of the given length.
|
// hex bytes for an instrction of the given length.
|
||||||
func bytesString(byte0, byte1, byte2 byte, length int) string {
|
func bytesString(byte0, byte1, byte2 byte, length int) string {
|
||||||
switch length {
|
switch length {
|
||||||
case 1:
|
case 1:
|
||||||
return fmt.Sprintf("%02X ", byte0)
|
return fmt.Sprintf("%02X", byte0)
|
||||||
case 2:
|
case 2:
|
||||||
return fmt.Sprintf("%02X %02X ", byte0, byte1)
|
return fmt.Sprintf("%02X %02X", byte0, byte1)
|
||||||
case 3:
|
case 3:
|
||||||
return fmt.Sprintf("%02X %02X %02X", byte0, byte1, byte2)
|
return fmt.Sprintf("%02X %02X %02X", byte0, byte1, byte2)
|
||||||
}
|
}
|
||||||
panic("Length must be 1, 2, or 3")
|
panic("Length must be 1, 2, or 3")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Symbols) addr4(addr uint16, lookback int) string {
|
||||||
|
if n := s.name(int(addr), lookback); n != "" {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("$%04X", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Symbols) addr2(addr byte, lookback int) string {
|
||||||
|
if n := s.name(int(addr), lookback); n != "" {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("$%02X", addr)
|
||||||
|
}
|
||||||
|
|
||||||
// addrString returns the address part of a 6502 assembly language
|
// addrString returns the address part of a 6502 assembly language
|
||||||
// instruction.
|
// instruction.
|
||||||
func addrString(pc uint16, byte1, byte2 byte, length int, mode opcodes.AddressingMode) string {
|
func addrString(pc uint16, byte1, byte2 byte, length int, mode opcodes.AddressingMode, s Symbols, lookback int) string {
|
||||||
addr16 := uint16(byte1) + uint16(byte2)<<8
|
addr16 := uint16(byte1) + uint16(byte2)<<8
|
||||||
addrRel := uint16(int32(pc+2) + int32(int8(byte1)))
|
addrRel := uint16(int32(pc+2) + int32(int8(byte1)))
|
||||||
switch mode {
|
switch mode {
|
||||||
case opcodes.MODE_IMPLIED:
|
case opcodes.MODE_IMPLIED, opcodes.MODE_A:
|
||||||
return " "
|
return ""
|
||||||
case opcodes.MODE_ABSOLUTE:
|
case opcodes.MODE_ABSOLUTE:
|
||||||
return fmt.Sprintf("$%04X ", addr16)
|
return fmt.Sprintf("%s", s.addr4(addr16, lookback))
|
||||||
case opcodes.MODE_INDIRECT:
|
case opcodes.MODE_INDIRECT:
|
||||||
return fmt.Sprintf("($%04X)", addr16)
|
return fmt.Sprintf("(%s)", s.addr4(addr16, lookback))
|
||||||
case opcodes.MODE_RELATIVE:
|
case opcodes.MODE_RELATIVE:
|
||||||
return fmt.Sprintf("$%04X ", addrRel)
|
return fmt.Sprintf("%s", s.addr4(addrRel, lookback))
|
||||||
case opcodes.MODE_IMMEDIATE:
|
case opcodes.MODE_IMMEDIATE:
|
||||||
return fmt.Sprintf("#$%02X ", byte1)
|
return fmt.Sprintf("#$%02X", byte1)
|
||||||
case opcodes.MODE_ABS_X:
|
case opcodes.MODE_ABS_X:
|
||||||
return fmt.Sprintf("$%04X,X", addr16)
|
return fmt.Sprintf("%s,X", s.addr4(addr16, lookback))
|
||||||
case opcodes.MODE_ABS_Y:
|
case opcodes.MODE_ABS_Y:
|
||||||
return fmt.Sprintf("$%04X,Y", addr16)
|
return fmt.Sprintf("%s,Y", s.addr4(addr16, lookback))
|
||||||
case opcodes.MODE_ZP:
|
case opcodes.MODE_ZP:
|
||||||
return fmt.Sprintf("$%02X ", byte1)
|
return fmt.Sprintf("%s", s.addr2(byte1, lookback))
|
||||||
case opcodes.MODE_ZP_X:
|
case opcodes.MODE_ZP_X:
|
||||||
return fmt.Sprintf("$%02X,X ", byte1)
|
return fmt.Sprintf("%s,X", s.addr2(byte1, lookback))
|
||||||
case opcodes.MODE_ZP_Y:
|
case opcodes.MODE_ZP_Y:
|
||||||
return fmt.Sprintf("$%02X,Y ", byte1)
|
return fmt.Sprintf("%s,Y", s.addr2(byte1, lookback))
|
||||||
case opcodes.MODE_INDIRECT_Y:
|
case opcodes.MODE_INDIRECT_Y:
|
||||||
return fmt.Sprintf("($%02X),Y", byte1)
|
return fmt.Sprintf("(%s),Y", s.addr2(byte1, lookback))
|
||||||
case opcodes.MODE_INDIRECT_X:
|
case opcodes.MODE_INDIRECT_X:
|
||||||
return fmt.Sprintf("($%02X,X)", byte1)
|
return fmt.Sprintf("(%s,X)", s.addr2(byte1, lookback))
|
||||||
case opcodes.MODE_A:
|
|
||||||
return " "
|
|
||||||
}
|
}
|
||||||
panic(fmt.Sprintf("Unknown op mode: %d", mode))
|
panic(fmt.Sprintf("Unknown op mode: %d", mode))
|
||||||
}
|
}
|
||||||
@ -64,19 +98,19 @@ func addrString(pc uint16, byte1, byte2 byte, length int, mode opcodes.Addressin
|
|||||||
// instruction. It returns the formatted bytes, the formatted
|
// instruction. It returns the formatted bytes, the formatted
|
||||||
// instruction and address, and the length. If it cannot find the
|
// instruction and address, and the length. If it cannot find the
|
||||||
// instruction, it returns a 1-byte "???" instruction.
|
// instruction, it returns a 1-byte "???" instruction.
|
||||||
func Disasm(pc uint16, byte0, byte1, byte2 byte) (string, string, int) {
|
func Disasm(pc uint16, byte0, byte1, byte2 byte, s Symbols, lookback int) (string, string, int) {
|
||||||
op, ok := opcodes.Opcodes[byte0]
|
op, ok := opcodes.Opcodes[byte0]
|
||||||
if !ok {
|
if !ok {
|
||||||
op = opcodes.NoOp
|
op = opcodes.NoOp
|
||||||
}
|
}
|
||||||
length := opcodes.ModeLengths[op.Mode]
|
length := opcodes.ModeLengths[op.Mode]
|
||||||
bytes := bytesString(byte0, byte1, byte2, length)
|
bytes := bytesString(byte0, byte1, byte2, length)
|
||||||
addr := addrString(pc, byte1, byte2, length, op.Mode)
|
addr := addrString(pc, byte1, byte2, length, op.Mode, s, lookback)
|
||||||
return bytes, op.Name + " " + addr, length
|
return bytes, op.Name + " " + addr, length
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisasmBlock disassembles an entire block, writing out the disassembly.
|
// DisasmBlock disassembles an entire block, writing out the disassembly.
|
||||||
func DisasmBlock(block []byte, startAddr uint16, w io.Writer) {
|
func DisasmBlock(block []byte, startAddr uint16, w io.Writer, s Symbols, lookback int, printLabels bool) {
|
||||||
l := len(block)
|
l := len(block)
|
||||||
for i := 0; i < l; i++ {
|
for i := 0; i < l; i++ {
|
||||||
byte0 := block[i]
|
byte0 := block[i]
|
||||||
@ -89,8 +123,46 @@ func DisasmBlock(block []byte, startAddr uint16, w io.Writer) {
|
|||||||
byte2 = block[i+2]
|
byte2 = block[i+2]
|
||||||
}
|
}
|
||||||
addr := uint16(i) + startAddr
|
addr := uint16(i) + startAddr
|
||||||
bytes, op, length := Disasm(addr, byte0, byte1, byte2)
|
bytes, op, length := Disasm(addr, byte0, byte1, byte2, s, lookback)
|
||||||
fmt.Fprintf(w, "$%04X: %s %s\n", addr, bytes, op)
|
if printLabels {
|
||||||
|
label, ok := s[int(addr)]
|
||||||
|
if ok {
|
||||||
|
fmt.Fprintf(w, "$%04X: %s:\n", addr, label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "$%04X: %-8s %s\n", addr, bytes, op)
|
||||||
i += length - 1
|
i += length - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var symRe = regexp.MustCompile(`(?i)^([0-9a-z_]+) *(?:.eq|equ|epz|=) *\$([0-9a-f]+)\b`)
|
||||||
|
|
||||||
|
func ReadSymbols(filename string) (Symbols, error) {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
s := make(Symbols)
|
||||||
|
lineNum := 0
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
lineNum++
|
||||||
|
groups := symRe.FindStringSubmatch(line)
|
||||||
|
if groups == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i, err := strconv.ParseInt(groups[2], 16, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%d: %s: %s", lineNum, line, err)
|
||||||
|
}
|
||||||
|
if i > 0xffff {
|
||||||
|
return nil, fmt.Errorf("%d: %s: Value %d out of range", lineNum, line, i)
|
||||||
|
}
|
||||||
|
s[int(i)] = groups[1]
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
26
asm/disasm_test.go
Normal file
26
asm/disasm_test.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package asm
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestSymbolRE(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
line string
|
||||||
|
label string
|
||||||
|
address string
|
||||||
|
}{
|
||||||
|
{"SHAPEL EQU $1A POINTER TO", "SHAPEL", "1A"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
groups := symRe.FindStringSubmatch(tt.line)
|
||||||
|
if groups == nil {
|
||||||
|
t.Errorf(`%d. Unable to parse '%s'`, i, tt.line)
|
||||||
|
}
|
||||||
|
if groups[1] != tt.label {
|
||||||
|
t.Errorf(`%d. want label='%s'; got '%s' for line: '%s'`, i, tt.label, groups[1], tt.line)
|
||||||
|
}
|
||||||
|
if groups[2] != tt.address {
|
||||||
|
t.Errorf(`%d. want address='%s'; got '%s' for line: '%s'`, i, tt.address, groups[2], tt.line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -132,7 +132,7 @@ func (c *cpu) Reset() {
|
|||||||
// status prints out the current CPU instruction and register status.
|
// status prints out the current CPU instruction and register status.
|
||||||
func status(c *cpu, m Memory) string {
|
func status(c *cpu, m Memory) string {
|
||||||
bytes, text, _ := asm.Disasm(c.PC(), m.Read(c.PC()), m.Read(c.PC()+1), m.Read(c.PC()+2))
|
bytes, text, _ := asm.Disasm(c.PC(), m.Read(c.PC()), m.Read(c.PC()+1), m.Read(c.PC()+2))
|
||||||
return fmt.Sprintf("$%04X: %s %s A=$%02X X=$%02X Y=$%02X SP=$%02X P=$%08b",
|
return fmt.Sprintf("$%04X: %-8s %-11s A=$%02X X=$%02X Y=$%02X SP=$%02X P=$%08b",
|
||||||
c.PC(), bytes, text, c.A(), c.X(), c.Y(), c.SP(), c.P())
|
c.PC(), bytes, text, c.A(), c.X(), c.Y(), c.SP(), c.P())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user