Skip to content
This repository has been archived by the owner on Jul 2, 2022. It is now read-only.

Francesco149/go-hachi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-hachi is a CHIP-8 emulator and disassembler written in Go. It is designed to be modular and pluggable into any front-end by simply implementing a syscall driver.

Currently, the emulator works for most standard CHIP-8 programs, but there are some bugs with very old programs, probably due to undocumented or legacy features that I'm not aware of. I'm also planning on adding super chip support.

I'm having a blast coding this and it's a great first emulation project.

Usage

First of all, get and build package hachi (the core):

go get github.com/Francesco149/go-hachi/hachi
go install github.com/Francesco149/go-hachi/hachi

Now you can build your desired front-end and associated driver. For now, the only available front-end is termloop.

go get github.com/Francesco149/go-hachi/drivers
go install github.com/Francesco149/go-hachi/drivers/termloop
go get github.com/Francesco149/go-hachi/tl-hachi
go install github.com/Francesco149/go-hachi/tl-hachi

Running the emulator is as easy as:

cd $GOPATH/bin
tl-hachi /path/to/program.ch8

For the default key bindings, check the driver's source file. The default ones for the termloop driver are:

{
	tl.KeyTab:        hachi.Key0,
	tl.KeyF2:         hachi.Key1,
	tl.KeyF3:         hachi.Key2,
	tl.KeyF4:         hachi.Key3,
	tl.KeyF5:         hachi.Key4,
	tl.KeyF6:         hachi.Key5,
	tl.KeyF7:         hachi.Key6,
	tl.KeyF8:         hachi.Key7,
	tl.KeyF9:         hachi.Key8,
	tl.KeyF10:        hachi.Key9,
	tl.KeyCtrlA:      hachi.KeyA,
	tl.KeyCtrlB:      hachi.KeyB,
	tl.KeyCtrlC:      hachi.KeyC,
	tl.KeyCtrlD:      hachi.KeyD,
	tl.KeyCtrlE:      hachi.KeyE,
	tl.KeyCtrlF:      hachi.KeyF,
	tl.KeyArrowDown:  hachi.Key2,
	tl.KeyArrowLeft:  hachi.Key4,
	tl.KeyArrowRight: hachi.Key6,
	tl.KeyArrowUp:    hachi.Key8,
	tl.KeyEnter:      hachi.Key5,
}

Implementing your own driver

package mydriver

import (
	"github.com/Francesco149/go-hachi/hachi"
	"log"
	"fmt"
)

type MyDriver struct {
	hachi.Driver
	// other fields
}

func (d *MyDriver) OnInit(c *hachi.Chip8) {
	// do init stuff
	log.Println("MyDriver initialized")
}

func (d *MyDriver) Cls() {
	// handle clear-screen call
	// NOTE: it's not recommended to actually clear the screen buffer here, as
	// it can cause glitches.
}

func (d *MyDriver) OnUpdate(c *hachi.Chip8) {
	// handle input and do other update logic
}

func (d *MyDriver) UpdateScreen(c *hachi.Chip8) {
	// handle draw call
}

func (d *MyDriver) Beep() { 
	// handle beep call
}

func (d *MyDriver) GetData(key string) interface{} {
	switch key {
		// ... (return any custom data your driver might require)
	}
	return nil
}

func (d *MyDriver) SetData(key string, value interface{}) error {
	switch key {
		// ... (set any custom data your driver might require)
	}
	return fmt.Errorf("Unknown data key '%s'.", key)
}

// -----------------------------------------------------------------------------

func init() {
	// register your driver
	err := hachi.RegisterDriver("mydriver", &MyDriver{})
	if err != nil {
		log.Fatal(err)
	}
}

Using a driver

package main

import (
	_ "path/to/mydriver"
	"github.com/Francesco149/go-hachi/hachi"
	"log"
	"os"
	"path/filepath"
)

func runEmulator(file string) (err error) {
	// initialize emulator
	ha, err := hachi.New("mydriver", nil)
	if err != nil {
		return
	}

	// load program
	_, err := ha.Load(file)
	if err != nil {
		return
	}

	return ha.Run()
}

func main() {
	log.SetOutput(os.Stdout)
	if len(os.Args) != 2 {
		log.Fatalf("Usage: %s path/to/program", filepath.Base(os.Args[0]))
		return
	}
	err := runEmulator(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}
}

Using the disassembler

Note that the disassembler only works for simple non-odd-aligned programs for now. I will eventually write a more advanced one.

package main

import (
	_ "path/to/mydriver"
	"github.com/Francesco149/go-hachi/hachi"
	"log"
	"os"
	"fmt"
	"path/filepath"
)

func runDisassembler(file string) (err error) {
	// initialize emulator
	ha, err := hachi.New("mydriver", nil)
	if err != nil {
		return
	}

	// load program
	progSize, err := ha.Load(file)
	if err != nil {
		return
	}

	// disassemble
	disassembly, err := hachi.DisassembleSimple(
		ha.Memory[0x200 : 0x200+progSize])
	if err != nil {
		return
	}

	// print disassembly
	for _, opcode := range disassembly {
		fmt.Println(opcode)
	}
}

func main() {
	log.SetOutput(os.Stdout)
	if len(os.Args) != 2 {
		log.Fatalf("Usage: %s path/to/program", filepath.Base(os.Args[0]))
		return
	}
	err := runDisassembler(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}
}

About

A CHIP-8 emulator written in go.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages