mirror of
https://github.com/zellyn/go6502.git
synced 2024-11-17 06:07:58 +00:00
97 lines
2.8 KiB
Go
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
|
|
}
|
|
}
|