From ba353efc34b39875619d637b675a68c0c6fdb8f0 Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Wed, 17 Jan 2024 19:34:35 -0600 Subject: [PATCH] Add HostContext interface for exposing host elements to the virtual hardware Not in love with the current interface yet. probably want to add logging methods rather than expose the logger itself, but i-i-i-i-terationnnnnn --- debug/gb_doctor.go | 3 +-- devices/host.go | 38 ++++++++++++++++++++++++++++ devices/serial_cable.go | 56 +++++++++++++++++++++++++++++++++++++++++ devices/serial_port.go | 41 +++++++++++------------------- hardware/dmg.go | 26 ++++++------------- main.go | 36 +++++++++++++++----------- 6 files changed, 138 insertions(+), 62 deletions(-) create mode 100644 devices/host.go create mode 100644 devices/serial_cable.go diff --git a/debug/gb_doctor.go b/debug/gb_doctor.go index a46d1c1..7f1be43 100644 --- a/debug/gb_doctor.go +++ b/debug/gb_doctor.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/maxfierke/gogo-gb/cpu" - "github.com/maxfierke/gogo-gb/devices" "github.com/maxfierke/gogo-gb/mem" ) @@ -28,7 +27,7 @@ func (gbd *GBDoctorDebugger) OnExecute(cpu *cpu.CPU, mmu *mem.MMU) { func (gbd *GBDoctorDebugger) OnInterrupt(cpu *cpu.CPU, mmu *mem.MMU) {} func (gbd *GBDoctorDebugger) OnRead(mmu *mem.MMU, addr uint16) mem.MemRead { - if addr == devices.REG_LCD_LY { + if addr == 0xFF44 { return mem.ReadReplace(0x90) // gameboy-doctor needs a stubbed out LCD } diff --git a/devices/host.go b/devices/host.go new file mode 100644 index 0000000..4024818 --- /dev/null +++ b/devices/host.go @@ -0,0 +1,38 @@ +package devices + +import ( + "log" +) + +type HostContext interface { + Logger() *log.Logger + SerialCable() SerialCable +} + +type Host struct { + logger *log.Logger + serialCable SerialCable +} + +func NewHost() *Host { + return &Host{ + logger: log.Default(), + serialCable: &NullSerialCable{}, + } +} + +func (h *Host) Logger() *log.Logger { + return h.logger +} + +func (h *Host) SetLogger(logger *log.Logger) { + h.logger = logger +} + +func (h *Host) SerialCable() SerialCable { + return h.serialCable +} + +func (h *Host) SetSerialCable(serialCable SerialCable) { + h.serialCable = serialCable +} diff --git a/devices/serial_cable.go b/devices/serial_cable.go new file mode 100644 index 0000000..202a0df --- /dev/null +++ b/devices/serial_cable.go @@ -0,0 +1,56 @@ +package devices + +import ( + "bytes" + "io" +) + +type SerialCable interface { + ReadByte() (byte, error) + WriteByte(value byte) error +} + +type NullSerialCable struct{} + +func (sc *NullSerialCable) ReadByte() (byte, error) { + return 0xFF, nil +} + +func (sc *NullSerialCable) WriteByte(value byte) error { + return nil +} + +type HostSerialCable struct { + reader io.Reader + writer io.Writer +} + +func NewHostSerialCable() *HostSerialCable { + return &HostSerialCable{ + reader: bytes.NewReader([]byte{}), + writer: io.Discard, + } +} + +func (sc *HostSerialCable) ReadByte() (byte, error) { + readBuf := []byte{0x00} + + if _, err := sc.reader.Read(readBuf); err != nil { + return 0xFF, err + } + + return readBuf[0], nil +} + +func (sc *HostSerialCable) WriteByte(value byte) error { + _, err := sc.writer.Write([]byte{value}) + return err +} + +func (sc *HostSerialCable) SetReader(reader io.Reader) { + sc.reader = reader +} + +func (sc *HostSerialCable) SetWriter(writer io.Writer) { + sc.writer = writer +} diff --git a/devices/serial_port.go b/devices/serial_port.go index 9942cc8..0e7e529 100644 --- a/devices/serial_port.go +++ b/devices/serial_port.go @@ -1,10 +1,7 @@ package devices import ( - "bytes" "fmt" - "io" - "log" "github.com/maxfierke/gogo-gb/mem" ) @@ -76,29 +73,19 @@ func (sc *SerialCtrl) SetClockInternal(enabled bool) { } type SerialPort struct { - clk uint - ctrl SerialCtrl - recv byte - buf byte - reader io.Reader - writer io.Writer + clk uint + ctrl SerialCtrl + recv byte + buf byte + host HostContext } -func NewSerialPort() *SerialPort { +func NewSerialPort(host HostContext) *SerialPort { return &SerialPort{ - reader: bytes.NewReader([]byte{}), - writer: io.Discard, + host: host, } } -func (sp *SerialPort) SetReader(reader io.Reader) { - sp.reader = reader -} - -func (sp *SerialPort) SetWriter(writer io.Writer) { - sp.writer = writer -} - func (sp *SerialPort) Step(cycles uint8, ic *InterruptController) { if !sp.ctrl.IsTransferEnabled() { return @@ -135,20 +122,22 @@ func (sp *SerialPort) OnWrite(mmu *mem.MMU, addr uint16, value byte) mem.MemWrit sp.ctrl.Write(value) if sp.ctrl.IsTransferEnabled() && sp.ctrl.IsClockInternal() { + cable := sp.host.SerialCable() + logger := sp.host.Logger() + // TODO(GBC): derive this somehow and factor in GBC speeds when relevant sp.clk = 8192 - _, err := sp.writer.Write([]byte{sp.buf}) + err := cable.WriteByte(sp.buf) if err != nil { - // TODO: Use logger from DMG here - log.Printf("Unable to write 0x%02X @ 0x%04X due to an error: %v\n", value, addr, err) + logger.Printf("Unable to write 0x%02X to serial cable: %v\n", value, err) } - readBuf := make([]byte, 1) - if bytesRead, err := sp.reader.Read(readBuf); err != nil || bytesRead == 0 { + recvVal, err := cable.ReadByte() + if err != nil { sp.recv = 0xFF } else { - sp.recv = readBuf[0] + sp.recv = recvVal } } diff --git a/hardware/dmg.go b/hardware/dmg.go index da10436..38510cd 100644 --- a/hardware/dmg.go +++ b/hardware/dmg.go @@ -1,7 +1,6 @@ package hardware import ( - "io" "log" "github.com/maxfierke/gogo-gb/cart" @@ -25,15 +24,15 @@ type DMG struct { // Non-components debugger debug.Debugger - logger *log.Logger + host devices.HostContext } -func NewDMG() (*DMG, error) { +func NewDMG(host devices.HostContext) (*DMG, error) { debugger := debug.NewNullDebugger() - return NewDMGDebug(debugger) + return NewDMGDebug(host, debugger) } -func NewDMGDebug(debugger debug.Debugger) (*DMG, error) { +func NewDMGDebug(host devices.HostContext, debugger debug.Debugger) (*DMG, error) { cpu, err := cpu.NewCPU() if err != nil { return nil, err @@ -42,7 +41,7 @@ func NewDMGDebug(debugger debug.Debugger) (*DMG, error) { cartridge := cart.NewCartridge() ic := devices.NewInterruptController() lcd := devices.NewLCD() - serial := devices.NewSerialPort() + serial := devices.NewSerialPort(host) timer := devices.NewTimer() ram := make([]byte, DMG_RAM_SIZE) @@ -72,6 +71,7 @@ func NewDMGDebug(debugger debug.Debugger) (*DMG, error) { cartridge: cartridge, debugger: debugger, ic: ic, + host: host, lcd: lcd, serial: serial, timer: timer, @@ -91,7 +91,7 @@ func (dmg *DMG) Step() bool { cycles, err := dmg.cpu.Step(dmg.mmu) if err != nil { - dmg.logger.Printf("Unexpected error while executing instruction: %v\n", err) + dmg.host.Logger().Printf("Unexpected error while executing instruction: %v\n", err) return false } @@ -110,15 +110,3 @@ func (dmg *DMG) Run() { dmg.debugger.OnExecute(dmg.cpu, dmg.mmu) } } - -func (dmg *DMG) SetLogger(logger *log.Logger) { - dmg.logger = logger -} - -func (dmg *DMG) SetSerialReader(serial io.Reader) { - dmg.serial.SetReader(serial) -} - -func (dmg *DMG) SetSerialWriter(serial io.Writer) { - dmg.serial.SetWriter(serial) -} diff --git a/main.go b/main.go index ad895ec..847ee9d 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "github.com/maxfierke/gogo-gb/cart" "github.com/maxfierke/gogo-gb/cpu/isa" "github.com/maxfierke/gogo-gb/debug" + "github.com/maxfierke/gogo-gb/devices" "github.com/maxfierke/gogo-gb/hardware" ) @@ -102,32 +103,37 @@ func debugPrintOpcodes(options *CLIOptions) { func initDMG(options *CLIOptions) *hardware.DMG { logger := options.logger - debugger, err := debug.NewDebugger(options.debugger) - if err != nil { - logger.Fatalf("Unable to initialize Debugger: %v\n", err) - } - - dmg, err := hardware.NewDMGDebug(debugger) - if err != nil { - logger.Fatalf("Unable to initialize DMG: %v\n", err) - } - - dmg.SetLogger(logger) + host := devices.NewHost() + host.SetLogger(logger) if options.serialPort != "" { + serialCable := devices.NewHostSerialCable() + if options.serialPort == "stdout" || options.serialPort == "/dev/stdout" { - dmg.SetSerialWriter(os.Stdout) + serialCable.SetWriter(os.Stdout) } else if options.serialPort == "stderr" || options.serialPort == "/dev/stderr" { - dmg.SetSerialWriter(os.Stderr) + serialCable.SetWriter(os.Stderr) } else { serialPort, err := os.Create(options.serialPort) if err != nil { logger.Fatalf("Unable to open file '%s' as serial port: %v\n", options.serialPort, err) } - dmg.SetSerialReader(serialPort) - dmg.SetSerialWriter(serialPort) + serialCable.SetReader(serialPort) + serialCable.SetWriter(serialPort) } + + host.SetSerialCable(serialCable) + } + + debugger, err := debug.NewDebugger(options.debugger) + if err != nil { + logger.Fatalf("Unable to initialize Debugger: %v\n", err) + } + + dmg, err := hardware.NewDMGDebug(host, debugger) + if err != nil { + logger.Fatalf("Unable to initialize DMG: %v\n", err) } return dmg