neon/cpu.go
2023-07-31 11:43:33 -03:00

106 lines
1.7 KiB
Go

package neon
import (
"fmt"
"math/rand"
"time"
)
type CPU struct {
// Accumulators:
A, B uint8
// Addresses:
PC uint16 // program counter
SP uint16 // stack pointer
// Flags:
T bool // test
C bool // carry
I bool // interrupt disable
J bool // jump disable
// Data:
RAM [0xFFFF + 1]byte
// Debugger hooks:
OnStep func(addr uint16, cd Condition, op Operator)
OnError func(error) Interrupt
// Subsystems:
Rand *rand.Rand
}
type Interrupt uint8
const NoInterrupt Interrupt = 0
func New() *CPU {
ns := time.Now().UnixNano()
src := rand.NewSource(ns)
return &CPU{
Rand: rand.New(src),
}
}
func (c *CPU) Load(data []byte) {
copy(c.RAM[:], data)
}
func (c *CPU) Step() Interrupt {
start := c.PC
next := c.RAM[start]
c.PC++
cd := Condition(next >> 6)
op := Operator(next & 0b00111111)
if c.OnStep != nil {
c.OnStep(start, cd, op)
}
data := operatorTable[op]
if data.step == nil {
return c.errorf("illegal operator 0x%02X", op)
}
addr := data.mode(c)
if !c.condition(cd) {
return NoInterrupt
}
return data.step(c, addr)
}
func (c *CPU) Run() Interrupt {
for {
interrupt := c.Step()
if interrupt != NoInterrupt {
return interrupt
}
}
}
func (c *CPU) get16(addr uint16) uint16 {
a := c.RAM[addr]
b := c.RAM[addr+1]
return uint16(b)<<8 | uint16(a)
}
func (c *CPU) condition(cd Condition) bool {
switch cd {
case Always:
return true
case IfTrue:
return c.T
case IfFalse:
return !c.T
case Randomly:
return c.Rand.Intn(2) == 1
default:
panic("unreachable")
}
}
func (c *CPU) errorf(format string, args ...any) Interrupt {
if c.OnError != nil {
return c.OnError(fmt.Errorf(format, args...))
}
return NoInterrupt
}