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