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

99 lines
1.9 KiB
Go
Raw Normal View History

2024-08-16 10:31:59 +00:00
package main
import (
"log"
"github.com/koron-go/z80"
)
// ioPeripheral is a z80.IO device at a given address.
type ioPeripheral struct {
start uint8
length uint8
peripheral z80.IO
}
// ioArbiter is a collection of I/O devices at addresses. It implements z80.IO
// itself, dispatching to the appropriate device as needed.
type ioArbiter struct {
peripherals []*ioPeripheral
}
func (i *ioArbiter) get(addr uint8) *ioPeripheral {
for _, periph := range i.peripherals {
if addr < periph.start {
continue
}
if addr > periph.start+periph.length {
continue
}
return periph
}
return nil
}
func (i *ioArbiter) In(addr uint8) uint8 {
p := i.get(addr)
if p == nil {
log.Fatalf("Unhandled I/O In at %02x", addr)
return 0
}
return p.peripheral.In(addr - p.start)
}
func (i *ioArbiter) Out(addr uint8, value uint8) {
p := i.get(addr)
if p == nil {
log.Fatalf("Unhandled I/O Out at %02x", addr)
return
}
p.peripheral.Out(addr-p.start, value)
}
// memory is a continuous memory region at a given address, optionally marked
// read only.
type memory struct {
start uint16
data []uint8
readonly bool
}
// memoryArbiter implements Z80.Memory and dispatches accesses to subordinate
// memory instances.
type memoryArbiter struct {
memories []memory
}
func (m *memoryArbiter) get(addr uint16) *memory {
for _, mem := range m.memories {
if addr < mem.start {
continue
}
if addr >= mem.start+uint16(len(mem.data)) {
continue
}
return &mem
}
return nil
}
func (m *memoryArbiter) Get(addr uint16) uint8 {
mem := m.get(addr)
if mem == nil {
log.Fatalf("Unhandled memory Get at %04x", addr)
return 0
}
return mem.data[addr-mem.start]
}
func (m *memoryArbiter) Set(addr uint16, value uint8) {
mem := m.get(addr)
if mem == nil {
log.Fatalf("Unhandled memory Set at %04x", addr)
return
}
if mem.readonly {
log.Fatalf("Read-only memory Set at %04x", addr)
}
mem.data[addr-mem.start] = value
}