mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2025-04-02 11:02:17 -04:00
193 lines
5.4 KiB
Go
193 lines
5.4 KiB
Go
// This file is part of Gopher2600.
|
|
//
|
|
// Gopher2600 is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Gopher2600 is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
|
|
//
|
|
// *** NOTE: all historical versions of this file, as found in any
|
|
// git repository, are also covered by the licence, even when this
|
|
// notice is not present ***
|
|
|
|
package display
|
|
|
|
import (
|
|
"fmt"
|
|
"gopher2600/hardware/cpu/execution"
|
|
"gopher2600/hardware/cpu/instructions"
|
|
"gopher2600/hardware/cpu/registers"
|
|
"gopher2600/symbols"
|
|
"strings"
|
|
)
|
|
|
|
// Instruction is the fully annotated, columnular representation of
|
|
// a instance of execution.Instruction
|
|
type Instruction struct {
|
|
Location string
|
|
Bytecode string
|
|
Address string
|
|
Mnemonic string
|
|
Operand string
|
|
Cycles string
|
|
Notes string
|
|
}
|
|
|
|
// Format execution.Result and create a new instance of Instruction
|
|
func Format(result execution.Result, symtable *symbols.Table) (*Instruction, error) {
|
|
if symtable == nil {
|
|
symtable = &symbols.Table{}
|
|
}
|
|
|
|
d := &Instruction{}
|
|
|
|
// if the operator hasn't been decoded yet then use placeholder strings in
|
|
// key columns
|
|
if result.Defn == nil {
|
|
d.Mnemonic = "???"
|
|
d.Operand = "?"
|
|
return d, nil
|
|
}
|
|
|
|
d.Address = fmt.Sprintf("0x%04x", result.Address)
|
|
|
|
if v, ok := symtable.Locations.Symbols[result.Address]; ok {
|
|
d.Location = v
|
|
}
|
|
|
|
d.Mnemonic = result.Defn.Mnemonic
|
|
|
|
// operands
|
|
var operand uint16
|
|
|
|
switch result.InstructionData.(type) {
|
|
case uint8:
|
|
operand = uint16(result.InstructionData.(uint8))
|
|
d.Operand = fmt.Sprintf("$%02x", operand)
|
|
case uint16:
|
|
operand = uint16(result.InstructionData.(uint16))
|
|
d.Operand = fmt.Sprintf("$%04x", operand)
|
|
case nil:
|
|
if result.Defn.Bytes == 2 {
|
|
d.Operand = "??"
|
|
} else if result.Defn.Bytes == 3 {
|
|
d.Operand = "????"
|
|
}
|
|
}
|
|
|
|
// Bytecode
|
|
if result.Final {
|
|
switch result.Defn.Bytes {
|
|
case 3:
|
|
d.Bytecode = fmt.Sprintf("%02x", operand&0xff00>>8)
|
|
fallthrough
|
|
case 2:
|
|
d.Bytecode = fmt.Sprintf("%02x %s", operand&0x00ff, d.Bytecode)
|
|
fallthrough
|
|
case 1:
|
|
d.Bytecode = fmt.Sprintf("%02x %s", result.Defn.OpCode, d.Bytecode)
|
|
default:
|
|
d.Bytecode = fmt.Sprintf("(%d bytes) %s", result.Defn.Bytes, d.Bytecode)
|
|
}
|
|
|
|
d.Bytecode = strings.TrimSpace(d.Bytecode)
|
|
}
|
|
|
|
// ... and use assembler symbol for the operand if available/appropriate
|
|
if result.InstructionData != nil && (d.Operand == "" || d.Operand[0] != '?') {
|
|
if result.Defn.AddressingMode != instructions.Immediate {
|
|
|
|
switch result.Defn.Effect {
|
|
case instructions.Flow:
|
|
if result.Defn.AddressingMode == instructions.Relative {
|
|
// relative labels. to get the correct label we have to
|
|
// simulate what a successful branch instruction would do:
|
|
|
|
// -- we create a mock register with the instruction's
|
|
// address as the initial value
|
|
pc := registers.NewProgramCounter(result.Address)
|
|
|
|
// -- add the number of instruction bytes to get the PC as
|
|
// it would be at the end of the instruction
|
|
pc.Add(uint16(result.Defn.Bytes))
|
|
|
|
// -- because we're doing 16 bit arithmetic with an 8bit
|
|
// value, we need to make sure the sign bit has been
|
|
// propogated to the more-significant bits
|
|
if operand&0x0080 == 0x0080 {
|
|
operand |= 0xff00
|
|
}
|
|
|
|
// -- add the 2s-complement value to the mock program
|
|
// counter
|
|
pc.Add(operand)
|
|
|
|
// -- look up mock program counter value in symbol table
|
|
if v, ok := symtable.Locations.Symbols[pc.Address()]; ok {
|
|
d.Operand = v
|
|
}
|
|
|
|
} else {
|
|
if v, ok := symtable.Locations.Symbols[operand]; ok {
|
|
d.Operand = v
|
|
}
|
|
}
|
|
case instructions.Read:
|
|
if v, ok := symtable.Read.Symbols[operand]; ok {
|
|
d.Operand = v
|
|
}
|
|
case instructions.Write:
|
|
fallthrough
|
|
case instructions.RMW:
|
|
if v, ok := symtable.Write.Symbols[operand]; ok {
|
|
d.Operand = v
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// decorate operand with addressing mode indicators
|
|
switch result.Defn.AddressingMode {
|
|
case instructions.Implied:
|
|
case instructions.Immediate:
|
|
d.Operand = fmt.Sprintf("#%s", d.Operand)
|
|
case instructions.Relative:
|
|
case instructions.Absolute:
|
|
case instructions.ZeroPage:
|
|
case instructions.Indirect:
|
|
d.Operand = fmt.Sprintf("(%s)", d.Operand)
|
|
case instructions.PreIndexedIndirect:
|
|
d.Operand = fmt.Sprintf("(%s,X)", d.Operand)
|
|
case instructions.PostIndexedIndirect:
|
|
d.Operand = fmt.Sprintf("(%s),Y", d.Operand)
|
|
case instructions.AbsoluteIndexedX:
|
|
d.Operand = fmt.Sprintf("%s,X", d.Operand)
|
|
case instructions.AbsoluteIndexedY:
|
|
d.Operand = fmt.Sprintf("%s,Y", d.Operand)
|
|
case instructions.IndexedZeroPageX:
|
|
d.Operand = fmt.Sprintf("%s,X", d.Operand)
|
|
case instructions.IndexedZeroPageY:
|
|
d.Operand = fmt.Sprintf("%s,Y", d.Operand)
|
|
default:
|
|
}
|
|
|
|
// cycles
|
|
d.Cycles = fmt.Sprintf("%d", result.ActualCycles)
|
|
|
|
// notes
|
|
if result.PageFault {
|
|
d.Notes = fmt.Sprintf("%s page-fault", d.Notes)
|
|
}
|
|
if result.Bug != "" {
|
|
d.Notes = fmt.Sprintf("%s * %s *", d.Notes, result.Bug)
|
|
}
|
|
|
|
return d, nil
|
|
}
|