jeol-t330a/modules/ph00524/emu/controller.go

158 lines
2.8 KiB
Go
Raw Normal View History

2024-08-16 10:31:59 +00:00
package main
import (
"log"
"os"
"time"
"github.com/koron-go/z80"
)
// evacuationController is the PH00524 board emulated in a synchronous step
// manner.
type evacuationController struct {
cpu *z80.CPU
m1 *motor
m2 *motor
u8 *i8255
u9 *i8255
// lastIOPC is the last PC on which an I/O operation (through U8 or U9) was
// performed. This is used for status/debugging.
lastIOPC uint16
}
func newEvacuationController(romPath string) *evacuationController {
rom, err := os.ReadFile(romPath)
if err != nil {
log.Fatalf("Failed to read ROM: %v", err)
}
mem := memoryArbiter{
memories: []memory{
{start: 0x0000, data: rom, readonly: true},
{start: 0x8000, data: make([]byte, 512)},
},
}
u8 := i8255{}
u9 := i8255{}
io := ioArbiter{
peripherals: []*ioPeripheral{
{start: 0x40, length: 4, peripheral: &u8},
{start: 0x80, length: 4, peripheral: &u9},
},
}
m1 := motor{barrier: &barrier{npos: 4}}
m2 := motor{barrier: &barrier{npos: 2}}
cpu := z80.CPU{
Memory: &mem,
IO: &io,
}
res := &evacuationController{
cpu: &cpu,
m1: &m1,
m2: &m2,
u8: &u8,
u9: &u9,
}
// Make lastIOPC get updated on u8/u9 I/O.
u8.onIn = res.onIO
u8.onOut = res.onIO
u9.onIn = res.onIO
u9.onOut = res.onIO
// Expansion bus connector, seems to be always pulled up.
u9.pa = 0xff
return res
}
func (e *evacuationController) onIO(_ i8255Port) {
e.lastIOPC = e.cpu.PC
}
func (e *evacuationController) getIndcators() indicators {
return indicators{
indEvac: (e.u8.pc & 1) != 0,
indHTReadyN: (e.u8.pc & 2) != 0,
indPumpDown: (e.u8.pc & 4) != 0,
}
}
func (e *evacuationController) pressPumpdown(value bool) {
if value {
e.u8.pb |= 1
} else {
e.u8.pb &= (0xff ^ 1)
}
}
func (e *evacuationController) pressVent(value bool) {
if value {
e.u8.pb |= 2
} else {
e.u8.pb &= (0xff ^ 2)
}
}
// step through the simulation/emulation given a delta time.
func (e *evacuationController) step(dt time.Duration) bool {
e.cpu.Step()
if e.cpu.HALT {
return false
}
// Simulate motors and barriers.
e.m1.dir = (e.u9.pc & 16) != 0
e.m1.en = (e.u9.pc & 4) != 0
e.m2.dir = (e.u9.pc & 8) != 0
e.m2.en = (e.u9.pc & 2) != 0
e.m1.sim(dt)
e.m2.sim(dt)
// Feed barrier status back into CPU.
b1p := e.m1.barrier.pins()
b2p := e.m2.barrier.pins()
e.u8.pa = 0
if b1p[0] {
e.u8.pa |= (1 << 5)
}
if b1p[1] {
e.u8.pa |= (1 << 4)
}
if b1p[2] {
e.u8.pa |= (1 << 3)
}
if b1p[3] {
e.u8.pa |= (1 << 2)
}
if b2p[0] {
e.u8.pa |= (1 << 1)
}
if b2p[1] {
e.u8.pa |= (1 << 0)
}
// Simulate vacuum gauge / comparators given light barrier status.
if b1p[3] {
// half succ (PRE_EVAC)
e.u8.pb |= 1 << 5
// full succ (???)
e.u8.pb |= 1 << 6
}
if !b1p[0] {
// no more succ
e.u8.pb &= 0xff ^ (1 << 5)
e.u8.pb &= 0xff ^ (1 << 6)
}
return true
}