Skip to content

Commit

Permalink
Merge pull request #9 from maxfierke/mf-cartridge_saves
Browse files Browse the repository at this point in the history
Implement cartridge SRAM save/restore
  • Loading branch information
maxfierke authored Jan 2, 2025
2 parents 23f4f29 + 88a956e commit 500104e
Show file tree
Hide file tree
Showing 11 changed files with 273 additions and 22 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# gogo-gb
a gameboy emulator for funsies

Current status: Games are playable, but slow. Graphics are buggy. No audio.

## TODO

- [X] Pass all of Blargg's `cpu_instrs` ROMs via `gameboy-doctor` (expect `02-interrupts.gb`, which isn't verifyable via `gameboy-doctor`)
Expand All @@ -15,9 +17,10 @@ a gameboy emulator for funsies
- [ ] Pass all of Blargg's `mem_timing-2` ROMs (manually verified)
- [X] Implement Joypad
- [ ] Implement RTC for MBC3
- [ ] Implement SRAM save & restore
- [X] Implement SRAM save & restore
- [ ] Pass `dmg-acid2` test ROM
- [ ] Implement Sound/APU
- [ ] Implement GBC

## Maybe Never?

Expand All @@ -26,6 +29,7 @@ Just being realistic about my likelihood of getting to these:
- [ ] FIFO-based rendering PPU (currently scanline)
- [ ] Implement emulation for every known DMG bug
- [ ] Implement SGB mode
- [ ] Implement MBC2
- [ ] Implement MBC6
- [ ] Implement MBC7
- [ ] Implement any multicarts or Hudson carts
Expand Down
11 changes: 10 additions & 1 deletion cart/cartridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"fmt"
"io"
"log"

"github.com/maxfierke/gogo-gb/cart/mbc"
Expand All @@ -17,7 +18,7 @@ var (

type Cartridge struct {
Header Header
mbc mem.MemHandler
mbc mbc.MBC
}

func NewCartridge() *Cartridge {
Expand Down Expand Up @@ -81,6 +82,14 @@ func (c *Cartridge) LoadCartridge(r *Reader) error {
return nil
}

func (c *Cartridge) Save(w io.Writer) error {
return c.mbc.Save(w)
}

func (c *Cartridge) LoadSave(r io.Reader) error {
return c.mbc.LoadSave(r)
}

func (c *Cartridge) OnRead(mmu *mem.MMU, addr uint16) mem.MemRead {
if c.mbc == nil {
return mem.ReadPassthrough()
Expand Down
4 changes: 4 additions & 0 deletions cart/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ func (hdr *Header) RamSizeBytes() uint {
}
}

func (hdr *Header) SupportsSaving() bool {
return hdr.RamSizeBytes() > 0
}

func (hdr *Header) DebugPrint(logger *log.Logger) {
logger.Printf("== Cartridge Info ==\n")
logger.Printf("\n")
Expand Down
12 changes: 11 additions & 1 deletion cart/mbc/common.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package mbc

import "github.com/maxfierke/gogo-gb/mem"
import (
"io"

"github.com/maxfierke/gogo-gb/mem"
)

const (
RAM_BANK_SIZE = 0x2000
Expand All @@ -18,3 +22,9 @@ func writeBankAddr(memory []byte, banksRegion mem.MemRegion, bankSize uint16, cu
bankSlotAddr := uint(addr) - uint(banksRegion.Start)
memory[bankBaseAddr+bankSlotAddr] = value
}

type MBC interface {
mem.MemHandler
Save(w io.Writer) error
LoadSave(r io.Reader) error
}
11 changes: 11 additions & 0 deletions cart/mbc/mbc0.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package mbc

import (
"fmt"
"io"

"github.com/maxfierke/gogo-gb/mem"
)
Expand All @@ -15,6 +16,8 @@ type MBC0 struct {
rom []byte
}

var _ MBC = (*MBC0)(nil)

func NewMBC0(rom []byte) *MBC0 {
return &MBC0{rom: rom}
}
Expand All @@ -40,3 +43,11 @@ func (m *MBC0) OnWrite(mmu *mem.MMU, addr uint16, value byte) mem.MemWrite {

panic(fmt.Sprintf("Attempting to write 0x%02X @ 0x%04X, which is out-of-bounds for MBC0", value, addr))
}

func (m *MBC0) Save(w io.Writer) error {
return nil
}

func (m *MBC0) LoadSave(r io.Reader) error {
return nil
}
29 changes: 29 additions & 0 deletions cart/mbc/mbc1.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package mbc

import (
"fmt"
"io"

"github.com/maxfierke/gogo-gb/mem"
)
Expand Down Expand Up @@ -34,6 +35,8 @@ type MBC1 struct {
rom []byte
}

var _ MBC = (*MBC1)(nil)

func NewMBC1(rom []byte, ram []byte) *MBC1 {
return &MBC1{
curRamBank: 0,
Expand Down Expand Up @@ -128,3 +131,29 @@ func (m *MBC1) OnWrite(mmu *mem.MMU, addr uint16, value byte) mem.MemWrite {

panic(fmt.Sprintf("Attempting to write 0x%02X @ 0x%04X, which is out-of-bounds for MBC1", value, addr))
}

func (m *MBC1) Save(w io.Writer) error {
if len(m.ram) == 0 {
return nil
}

n, err := w.Write(m.ram)
if err != nil {
return fmt.Errorf("mbc1: saving SRAM: %w. wrote %d bytes", err, n)
}

return nil
}

func (m *MBC1) LoadSave(r io.Reader) error {
if len(m.ram) == 0 {
return nil
}

n, err := io.ReadFull(r, m.ram)
if err != nil {
return fmt.Errorf("mbc1: loading save into SRAM: %w. read %d bytes", err, n)
}

return nil
}
41 changes: 41 additions & 0 deletions cart/mbc/mbc3.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package mbc

import (
"fmt"
"io"

"github.com/maxfierke/gogo-gb/mem"
)
Expand Down Expand Up @@ -60,6 +61,8 @@ type MBC3 struct {
rtcDaysOverflow bool
}

var _ MBC = (*MBC3)(nil)

func NewMBC3(rom []byte, ram []byte, rtcAvailable bool) *MBC3 {
return &MBC3{
ram: ram,
Expand Down Expand Up @@ -210,6 +213,36 @@ func (m *MBC3) writeRtcReg(reg mbc3RtcReg, value byte) {
}
}

func (m *MBC3) Save(w io.Writer) error {
if len(m.ram) == 0 {
return nil
}

n, err := w.Write(m.ram)
if err != nil {
return fmt.Errorf("mbc3: saving SRAM: %w. wrote %d bytes", err, n)
}

// TODO: Write RTC registers

return nil
}

func (m *MBC3) LoadSave(r io.Reader) error {
if len(m.ram) == 0 {
return nil
}

n, err := io.ReadFull(r, m.ram)
if err != nil {
return fmt.Errorf("mbc3: loading save into SRAM: %w. read %d bytes", err, n)
}

// TODO: Read RTC registers

return nil
}

var (
MBC30_ROM_BANKS = mem.MemRegion{Start: 0x4000, End: 0x7FFF}

Expand Down Expand Up @@ -269,3 +302,11 @@ func (m *MBC30) OnWrite(mmu *mem.MMU, addr uint16, value byte) mem.MemWrite {

return m.MBC3.OnWrite(mmu, addr, value)
}

func (m *MBC30) Save(w io.Writer) error {
return m.MBC3.Save(w)
}

func (m *MBC30) LoadSave(r io.Reader) error {
return m.MBC3.LoadSave(r)
}
29 changes: 29 additions & 0 deletions cart/mbc/mbc5.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package mbc

import (
"fmt"
"io"

"github.com/maxfierke/gogo-gb/mem"
)
Expand Down Expand Up @@ -34,6 +35,8 @@ type MBC5 struct {
rom []byte
}

var _ MBC = (*MBC5)(nil)

func NewMBC5(rom []byte, ram []byte) *MBC5 {
return &MBC5{
curRamBank: 0,
Expand Down Expand Up @@ -114,3 +117,29 @@ func (m *MBC5) OnWrite(mmu *mem.MMU, addr uint16, value byte) mem.MemWrite {

panic(fmt.Sprintf("Attempting to write 0x%02X @ 0x%04X, which is out-of-bounds for MBC5", value, addr))
}

func (m *MBC5) Save(w io.Writer) error {
if len(m.ram) == 0 {
return nil
}

n, err := w.Write(m.ram)
if err != nil {
return fmt.Errorf("mbc5: saving SRAM: %w. wrote %d bytes", err, n)
}

return nil
}

func (m *MBC5) LoadSave(r io.Reader) error {
if len(m.ram) == 0 {
return nil
}

n, err := io.ReadFull(r, m.ram)
if err != nil {
return fmt.Errorf("mbc5: loading save into SRAM: %w. read %d bytes", err, n)
}

return nil
}
5 changes: 5 additions & 0 deletions hardware/console.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package hardware

import (
"io"

"github.com/maxfierke/gogo-gb/cart"
"github.com/maxfierke/gogo-gb/debug"
"github.com/maxfierke/gogo-gb/devices"
Expand All @@ -9,7 +11,10 @@ import (
type Console interface {
AttachDebugger(debugger debug.Debugger)
DetachDebugger()
CartridgeHeader() cart.Header
LoadCartridge(r *cart.Reader) error
Save(w io.Writer) error
LoadSave(r io.Reader) error
Step() error
Run(host devices.HostInterface) error
}
33 changes: 32 additions & 1 deletion hardware/dmg.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,39 @@ func (dmg *DMG) DetachDebugger() {
dmg.debugger = debug.NewNullDebugger()
}

func (dmg *DMG) CartridgeHeader() cart.Header {
if dmg.cartridge == nil {
return cart.Header{}
}

return dmg.cartridge.Header
}

func (dmg *DMG) LoadCartridge(r *cart.Reader) error {
return dmg.cartridge.LoadCartridge(r)
err := dmg.cartridge.LoadCartridge(r)
if err != nil {
return fmt.Errorf("dmg: loading cartridge: %w", err)
}

return nil
}

func (dmg *DMG) LoadSave(r io.Reader) error {
err := dmg.cartridge.LoadSave(r)
if err != nil {
return fmt.Errorf("dmg: loading save: %w", err)
}

return nil
}

func (dmg *DMG) Save(w io.Writer) error {
err := dmg.cartridge.Save(w)
if err != nil {
return fmt.Errorf("dmg: writing save: %w", err)
}

return nil
}

func (dmg *DMG) DebugPrint(logger *log.Logger) {
Expand Down
Loading

0 comments on commit 500104e

Please sign in to comment.