1
0
mirror of https://github.com/zellyn/go6502.git synced 2024-11-19 03:04:46 +00:00
go6502/asm/disasm.go
2013-04-21 16:49:28 -07:00

97 lines
2.8 KiB
Go

/*
Package asm provides routines for decompiling 6502 assembly language.
*/
package asm
import (
"fmt"
"io"
"github.com/zellyn/go6502/opcodes"
)
// 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)
case 2:
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")
}
// addrString returns the address part of a 6502 assembly language
// instruction.
func addrString(pc uint16, byte1, byte2 byte, length int, mode opcodes.AddressingMode) 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_ABSOLUTE:
return fmt.Sprintf("$%04X ", addr16)
case opcodes.MODE_INDIRECT:
return fmt.Sprintf("($%04X)", addr16)
case opcodes.MODE_RELATIVE:
return fmt.Sprintf("$%04X ", addrRel)
case opcodes.MODE_IMMEDIATE:
return fmt.Sprintf("#$%02X ", byte1)
case opcodes.MODE_ABS_X:
return fmt.Sprintf("$%04X,X", addr16)
case opcodes.MODE_ABS_Y:
return fmt.Sprintf("$%04X,Y", addr16)
case opcodes.MODE_ZP:
return fmt.Sprintf("$%02X ", byte1)
case opcodes.MODE_ZP_X:
return fmt.Sprintf("$%02X,X ", byte1)
case opcodes.MODE_ZP_Y:
return fmt.Sprintf("$%02X,Y ", byte1)
case opcodes.MODE_INDIRECT_Y:
return fmt.Sprintf("($%02X),Y", byte1)
case opcodes.MODE_INDIRECT_X:
return fmt.Sprintf("($%02X,X)", byte1)
case opcodes.MODE_A:
return " "
}
panic(fmt.Sprintf("Unknown op mode: %d", mode))
}
// Disasm disassembles a single (up to three byte) 6502
// 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) {
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)
return bytes, op.Name + " " + addr, length
}
// DisasmBlock disassembles an entire block, writing out the disassembly.
func DisasmBlock(block []byte, startAddr uint16, w io.Writer) {
l := len(block)
for i := 0; i < l; i++ {
byte0 := block[i]
byte1 := byte(0xFF)
byte2 := byte(0xFF)
if i+1 < l {
byte1 = block[i+1]
}
if i+2 < l {
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)
i += length - 1
}
}