You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
122 lines
2.1 KiB
Go
122 lines
2.1 KiB
Go
package code
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
)
|
|
|
|
type Instructions []byte
|
|
|
|
func (ins Instructions) String() string {
|
|
var out bytes.Buffer
|
|
|
|
i := 0
|
|
for i < len(ins) {
|
|
def, err := Lookup(ins[i])
|
|
if err != nil {
|
|
fmt.Fprintf(&out, "ERROR: %s\n", err)
|
|
continue
|
|
}
|
|
|
|
operands, read := ReadOperands(def, ins[i+1:])
|
|
|
|
fmt.Fprintf(&out, "%04d %s\n", i, ins.fmtInstruction(def, operands))
|
|
|
|
i += 1 + read
|
|
}
|
|
|
|
return out.String()
|
|
}
|
|
|
|
func (ins Instructions) fmtInstruction(def *Definition, operands []int) string {
|
|
operandCount := len(def.OperandWidths)
|
|
|
|
if len(operands) != operandCount {
|
|
return fmt.Sprintf("ERROR: operand len %d does not match defined %d\n",
|
|
len(operands), operandCount)
|
|
}
|
|
|
|
switch operandCount {
|
|
case 0:
|
|
return def.Name
|
|
case 1:
|
|
return fmt.Sprintf("%s %d", def.Name, operands[0])
|
|
}
|
|
|
|
return fmt.Sprintf("ERROR: unhandled operandCount for %s\n", def.Name)
|
|
}
|
|
|
|
type Opcode byte
|
|
|
|
const (
|
|
OpConstant Opcode = iota
|
|
OpAdd
|
|
)
|
|
|
|
type Definition struct {
|
|
Name string
|
|
OperandWidths []int
|
|
}
|
|
|
|
var definitions = map[Opcode]*Definition{
|
|
OpConstant: {"OpConstant", []int{2}},
|
|
OpAdd: {"OpAdd", []int{}},
|
|
}
|
|
|
|
func Lookup(op byte) (*Definition, error) {
|
|
def, ok := definitions[Opcode(op)]
|
|
if !ok {
|
|
return nil, fmt.Errorf("opcode %d undefined", op)
|
|
}
|
|
|
|
return def, nil
|
|
}
|
|
|
|
func Make(op Opcode, operands ...int) []byte {
|
|
def, ok := definitions[op]
|
|
if !ok {
|
|
return []byte{}
|
|
}
|
|
|
|
instructionLen := 1
|
|
for _, w := range def.OperandWidths {
|
|
instructionLen += w
|
|
}
|
|
|
|
instruction := make([]byte, instructionLen)
|
|
instruction[0] = byte(op)
|
|
|
|
offset := 1
|
|
for i, o := range operands {
|
|
width := def.OperandWidths[i]
|
|
switch width {
|
|
case 2:
|
|
binary.BigEndian.PutUint16(instruction[offset:], uint16(o))
|
|
}
|
|
offset += width
|
|
}
|
|
|
|
return instruction
|
|
}
|
|
|
|
func ReadOperands(def *Definition, ins Instructions) ([]int, int) {
|
|
operands := make([]int, len(def.OperandWidths))
|
|
offset := 0
|
|
|
|
for i, width := range def.OperandWidths {
|
|
switch width {
|
|
case 2:
|
|
operands[i] = int(ReadUint16(ins[offset:]))
|
|
}
|
|
|
|
offset += width
|
|
}
|
|
|
|
return operands, offset
|
|
}
|
|
|
|
func ReadUint16(ins Instructions) uint16 {
|
|
return binary.BigEndian.Uint16(ins)
|
|
}
|