Files
zzg a22c99344e
ci / test (push) Canceled after 0s
initial commit
2026-09-25 22:12:55 -07:00

133 lines
2.9 KiB
Go

// Package x64 models the small subset of x86-64 that the calculator emits,
// and can execute it.
//
// A simulator exists because the emitted assembly must be testable
// everywhere, including on machines that are not x86-64. Assembling and
// running the output only works on an x86-64 host with a toolchain; the
// simulator checks the same instructions' semantics anywhere, so the test
// suite is meaningful in CI regardless of the runner's architecture.
package x64
import (
"fmt"
"strconv"
"strings"
)
// Register is a 64-bit general purpose register.
type Register string
const (
RAX Register = "rax"
RBX Register = "rbx"
RCX Register = "rcx"
RDX Register = "rdx"
RSI Register = "rsi"
RDI Register = "rdi"
RBP Register = "rbp"
RSP Register = "rsp"
R8 Register = "r8"
R9 Register = "r9"
R10 Register = "r10"
R11 Register = "r11"
)
// Op is an opcode in the emitted subset.
type Op string
const (
MOV Op = "mov"
ADD Op = "add"
SUB Op = "sub"
IMUL Op = "imul"
IDIV Op = "idiv"
NEG Op = "neg"
PUSH Op = "push"
POP Op = "pop"
CQO Op = "cqo" // sign-extend rax into rdx:rax, required before idiv
XCHG Op = "xchg"
RET Op = "ret"
)
// Operand is a register, an immediate, or a memory reference.
type Operand struct {
Reg Register
Imm int64
IsImm bool
IsMem bool
// Offset is the displacement for a memory operand, as in [rbp-8].
Offset int
Base Register
}
// R returns a register operand.
func R(r Register) Operand { return Operand{Reg: r} }
// I returns an immediate operand.
func I(v int64) Operand { return Operand{Imm: v, IsImm: true} }
// M returns a memory operand [base+offset].
func M(base Register, offset int) Operand {
return Operand{IsMem: true, Base: base, Offset: offset}
}
func (o Operand) String() string {
switch {
case o.IsImm:
return strconv.FormatInt(o.Imm, 10)
case o.IsMem:
if o.Offset == 0 {
return fmt.Sprintf("[%s]", o.Base)
}
if o.Offset > 0 {
return fmt.Sprintf("[%s+%d]", o.Base, o.Offset)
}
return fmt.Sprintf("[%s%d]", o.Base, o.Offset)
default:
return string(o.Reg)
}
}
// Instruction is one emitted instruction, optionally with a comment
// explaining why it is there.
type Instruction struct {
Op Op
Operands []Operand
Comment string
}
func (i Instruction) String() string {
var sb strings.Builder
sb.WriteString(" ")
sb.WriteString(string(i.Op))
for n, o := range i.Operands {
if n == 0 {
sb.WriteString(" ")
} else {
sb.WriteString(", ")
}
sb.WriteString(o.String())
}
if i.Comment != "" {
// Pad so comments line up, which makes the output readable.
for sb.Len() < 36 {
sb.WriteByte(' ')
}
sb.WriteString("; " + i.Comment)
}
return sb.String()
}
// Program is a sequence of instructions.
type Program []Instruction
// String renders the program as NASM-syntax assembly.
func (p Program) String() string {
var sb strings.Builder
for _, i := range p {
sb.WriteString(i.String())
sb.WriteByte('\n')
}
return sb.String()
}