From ba28865248c017358bc0ab9c624823eaefebbc89 Mon Sep 17 00:00:00 2001 From: Pau Costa Date: Tue, 23 Jul 2024 20:39:56 +0200 Subject: [PATCH] feat: rom loading, and character map --- emulator/chip8.go | 77 +++++++++++++++++++++++++++++++++++++++++ emulator/chip8_test.go | 21 +++++++++++ main.go | 40 +++++++++++++++++---- roms/ibm_logo.ch8 | Bin 0 -> 132 bytes 4 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 emulator/chip8.go create mode 100644 emulator/chip8_test.go create mode 100644 roms/ibm_logo.ch8 diff --git a/emulator/chip8.go b/emulator/chip8.go new file mode 100644 index 0000000..0dad9c6 --- /dev/null +++ b/emulator/chip8.go @@ -0,0 +1,77 @@ +package emulator + +import "errors" + +type CHIP8 struct { + memory [4096]byte + v [16]byte + i uint16 + pc uint16 + sp uint16 + delayTimer uint8 + soundTimer uint8 + stack [16]uint16 + keypad [16]bool + // To transform the linear Memory to a 2D array for easier drawing, + // having a (x, y) coordinate then (x, y) = graphics[x + (y * 64)] + graphics [64 * 32]bool + opcode uint16 + fonts [80]byte +} + +func NewChip8() *CHIP8 { + chip8 := &CHIP8{ + memory: [4096]byte{}, + v: [16]byte{}, + i: 0, + // The original 0x200 code of the CHIP-8 interpreter was from 0x0 to 0x200, + pc: 0x200, + sp: 0, + delayTimer: 0, + soundTimer: 0, + stack: [16]uint16{}, + keypad: [16]bool{}, + graphics: [64 * 32]bool{}, + opcode: 0, + fonts: [80]byte{}, + } + chip8.Initialize() + return chip8 +} + +func (c *CHIP8) Initialize() { + charMap := [80]byte{ + 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0 + 0x20, 0x60, 0x20, 0x20, 0x70, // 1 + 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2 + 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3 + 0x90, 0x90, 0xF0, 0x10, 0x10, // 4 + 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5 + 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6 + 0xF0, 0x10, 0x20, 0x40, 0x40, // 7 + 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8 + 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9 + 0xF0, 0x90, 0xF0, 0x90, 0x90, // A + 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B + 0xF0, 0x80, 0x80, 0x80, 0xF0, // C + 0xE0, 0x90, 0x90, 0x90, 0xE0, // D + 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E + 0xF0, 0x80, 0xF0, 0x80, 0x80, // F + } + + // Char map goes from 0x050 to 0x9F + for i, charByte := range charMap { + c.memory[i+0x50] = charByte + } +} + +func (c *CHIP8) LoadROMIntoMemory(dat []byte) error { + if len(dat) > len(c.memory)-0x200 { + return errors.New("ROM is too large to fit into memory") + } + // Roms start from 0x200 + for i, datByte := range dat { + c.memory[i+0x200] = datByte + } + return nil +} diff --git a/emulator/chip8_test.go b/emulator/chip8_test.go new file mode 100644 index 0000000..6abb295 --- /dev/null +++ b/emulator/chip8_test.go @@ -0,0 +1,21 @@ +package emulator + +import ( + "testing" +) + +func TestLoadRomIntoMemory(t *testing.T) { + chip8 := NewChip8() + romBytes := []byte{0xDE, 0xAD, 0xBE, 0xEF} + + _ = chip8.LoadROMIntoMemory(romBytes) + // We always expect roms to be loaded starting from 0x200 + ans := chip8.memory[0x200:0x204] + + for i, ansByte := range ans { + if romBytes[i] != ansByte { + t.Errorf("Expected %x, got %x", romBytes[i], ansByte) + } + } + +} diff --git a/main.go b/main.go index 15d70bf..f9a1d15 100644 --- a/main.go +++ b/main.go @@ -4,11 +4,17 @@ import ( "fmt" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/ebitenutil" + "github.com/micosilent/go_chip8/emulator" "log" + "os" + "time" ) type Game struct { - counter int + counter int + lastFrameTime time.Time + debugText string + emulator *emulator.CHIP8 } func (g *Game) Update() error { @@ -17,24 +23,46 @@ func (g *Game) Update() error { func newGame() *Game { return &Game{ - counter: 1, + counter: 1, + lastFrameTime: time.Now(), + debugText: "", + emulator: emulator.NewChip8(), + } +} + +func check(err error) { + if err != nil { + log.Fatal(err) } } func (g *Game) Draw(screen *ebiten.Image) { - ebitenutil.DebugPrint(screen, fmt.Sprintf("Counter: %d", g.counter)) + deltaTime := time.Since(g.lastFrameTime) + fps := int(1 / deltaTime.Seconds()) + g.debugText = fmt.Sprintf("Counter: %d, fps: %d", g.counter, fps) + ebitenutil.DebugPrint(screen, g.debugText) g.counter++ + g.lastFrameTime = time.Now() } func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { return 320, 240 } +func (g *Game) LoadROM(path string) { + dat, err := os.ReadFile(path) + check(err) + + err = g.emulator.LoadROMIntoMemory(dat) + check(err) +} + func main() { game := newGame() + game.LoadROM("roms/ibm_logo.ch8") ebiten.SetWindowSize(480, 320) ebiten.SetWindowTitle("Go CHIP8 Emulator") - if err := ebiten.RunGame(game); err != nil { - log.Fatal(err) - } + + err := ebiten.RunGame(game) + check(err) } diff --git a/roms/ibm_logo.ch8 b/roms/ibm_logo.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..113338e67097e0a2ad227e5b79b3a5d1a586a660 GIT binary patch literal 132 zcmZR0ut+O`Cz0cVd;#Yo%M0?0JPJT;mPO$}l4DUCm@K^@FQoCG;Xi{76av{Gt_6cV z5Ly6P{~H({0AT|Vg6SU&e;Dk5a@Gu%3_lotFf*_-@H6aZ_|5Q{;W2|VOes`300ZwW A9{>OV literal 0 HcmV?d00001