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 }