mirror of
https://github.com/zellyn/go6502.git
synced 2024-12-27 19:30:01 +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
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"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
|
||||
// hex bytes for an instrction of the given length.
|
||||
func bytesString(byte0, byte1, byte2 byte, length int) string {
|
||||
switch length {
|
||||
case 1:
|
||||
return fmt.Sprintf("%02X ", byte0)
|
||||
return fmt.Sprintf("%02X", byte0)
|
||||
case 2:
|
||||
return fmt.Sprintf("%02X %02X ", byte0, byte1)
|
||||
return fmt.Sprintf("%02X %02X", byte0, byte1)
|
||||
case 3:
|
||||
return fmt.Sprintf("%02X %02X %02X", byte0, byte1, byte2)
|
||||
}
|
||||
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
|
||||
// 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
|
||||
addrRel := uint16(int32(pc+2) + int32(int8(byte1)))
|
||||
switch mode {
|
||||
case opcodes.MODE_IMPLIED:
|
||||
return " "
|
||||
case opcodes.MODE_IMPLIED, opcodes.MODE_A:
|
||||
return ""
|
||||
case opcodes.MODE_ABSOLUTE:
|
||||
return fmt.Sprintf("$%04X ", addr16)
|
||||
return fmt.Sprintf("%s", s.addr4(addr16, lookback))
|
||||
case opcodes.MODE_INDIRECT:
|
||||
return fmt.Sprintf("($%04X)", addr16)
|
||||
return fmt.Sprintf("(%s)", s.addr4(addr16, lookback))
|
||||
case opcodes.MODE_RELATIVE:
|
||||
return fmt.Sprintf("$%04X ", addrRel)
|
||||
return fmt.Sprintf("%s", s.addr4(addrRel, lookback))
|
||||
case opcodes.MODE_IMMEDIATE:
|
||||
return fmt.Sprintf("#$%02X ", byte1)
|
||||
return fmt.Sprintf("#$%02X", byte1)
|
||||
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:
|
||||
return fmt.Sprintf("$%04X,Y", addr16)
|
||||
return fmt.Sprintf("%s,Y", s.addr4(addr16, lookback))
|
||||
case opcodes.MODE_ZP:
|
||||
return fmt.Sprintf("$%02X ", byte1)
|
||||
return fmt.Sprintf("%s", s.addr2(byte1, lookback))
|
||||
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:
|
||||
return fmt.Sprintf("$%02X,Y ", byte1)
|
||||
return fmt.Sprintf("%s,Y", s.addr2(byte1, lookback))
|
||||
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:
|
||||
return fmt.Sprintf("($%02X,X)", byte1)
|
||||
case opcodes.MODE_A:
|
||||
return " "
|
||||
return fmt.Sprintf("(%s,X)", s.addr2(byte1, lookback))
|
||||
}
|
||||
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 and address, and the length. If it cannot find the
|
||||
// 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]
|
||||
if !ok {
|
||||
op = opcodes.NoOp
|
||||
}
|
||||
length := opcodes.ModeLengths[op.Mode]
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
for i := 0; i < l; i++ {
|
||||
byte0 := block[i]
|
||||
@ -89,8 +123,46 @@ func DisasmBlock(block []byte, startAddr uint16, w io.Writer) {
|
||||
byte2 = block[i+2]
|
||||
}
|
||||
addr := uint16(i) + startAddr
|
||||
bytes, op, length := Disasm(addr, byte0, byte1, byte2)
|
||||
fmt.Fprintf(w, "$%04X: %s %s\n", addr, bytes, op)
|
||||
bytes, op, length := Disasm(addr, byte0, byte1, byte2, s, lookback)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
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))
|
||||
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())
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user