very basic libretro core (no sound + limited controls)
This commit is contained in:
parent
e6b150136c
commit
529672f874
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
|||
[submodule "libretro/libretro-common"]
|
||||
path = libretro/libretro-common
|
||||
url = https://github.com/libretro/libretro-common.git
|
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"libretro.h": "c"
|
||||
}
|
||||
}
|
2
Makefile
2
Makefile
|
@ -1,5 +1,5 @@
|
|||
TARGET=chip8_libretro
|
||||
LIBRETRO_CORE=$(TARGET).so
|
||||
LIBRETRO_CORE=$(TARGET).dylib
|
||||
LIBRETRO_HEADER=$(TARGET).h
|
||||
BUILD_VERSION=$(shell git rev-parse --short HEAD)
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ const (
|
|||
Width = 64
|
||||
Height = 32
|
||||
FPS = 60
|
||||
CyclesPerFrame = 8
|
||||
SampleRate = 44100
|
||||
)
|
||||
|
||||
|
@ -98,6 +99,13 @@ func (emulator *Emulator) LoadFont() {
|
|||
})
|
||||
}
|
||||
|
||||
func (emulator *Emulator) Update() {
|
||||
emulator.UpdateTimers()
|
||||
for cycle := 0; cycle < CyclesPerFrame; cycle++ {
|
||||
emulator.Cycle()
|
||||
}
|
||||
}
|
||||
|
||||
func (emulator *Emulator) UpdateTimers() {
|
||||
if emulator.ST != 0 {
|
||||
emulator.PlaySound()
|
||||
|
@ -110,6 +118,9 @@ func (emulator *Emulator) UpdateTimers() {
|
|||
|
||||
func (emulator *Emulator) Cycle() {
|
||||
instruction := binary.BigEndian.Uint16(emulator.Memory[emulator.PC:])
|
||||
if instruction == 0x0000 {
|
||||
return
|
||||
}
|
||||
emulator.PC += InstructionSize
|
||||
|
||||
nnn := instruction & 0x0FFF
|
||||
|
|
1
go.mod
1
go.mod
|
@ -4,6 +4,7 @@ go 1.17
|
|||
|
||||
require (
|
||||
github.com/hajimehoshi/ebiten/v2 v2.2.5
|
||||
github.com/lanzafame/bobblehat v0.0.0-20190628174408-a0f0792c1691
|
||||
github.com/stretchr/testify v1.7.0
|
||||
)
|
||||
|
||||
|
|
2
go.sum
2
go.sum
|
@ -20,6 +20,8 @@ github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3T
|
|||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lanzafame/bobblehat v0.0.0-20190628174408-a0f0792c1691 h1:o3aMwSNYkdM+w3zODsAQTGsP09c4r9E2DW42NjYRS0M=
|
||||
github.com/lanzafame/bobblehat v0.0.0-20190628174408-a0f0792c1691/go.mod h1:CuWZprWAvuwcm4Sl0Vt1qL8foyta9HIBRLC5qyYtUW4=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
#include "libretro-common/include/libretro.h"
|
||||
#include <string.h>
|
||||
|
||||
// RETRO_API void retro_set_environment(retro_environment_t);
|
||||
// RETRO_API void retro_set_video_refresh(retro_video_refresh_t);
|
||||
// RETRO_API void retro_set_audio_sample(retro_audio_sample_t);
|
||||
// RETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t);
|
||||
// RETRO_API void retro_set_input_poll(retro_input_poll_t);
|
||||
// RETRO_API void retro_set_input_state(retro_input_state_t);
|
||||
|
||||
RETRO_API void retro_init(void)
|
||||
{
|
||||
void Initialize(void);
|
||||
Initialize();
|
||||
}
|
||||
|
||||
RETRO_API void retro_deinit(void)
|
||||
{
|
||||
void Deinitialize(void);
|
||||
Deinitialize();
|
||||
}
|
||||
|
||||
RETRO_API unsigned retro_api_version(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
RETRO_API void retro_get_system_info(struct retro_system_info *info)
|
||||
{
|
||||
memset(info, 0, sizeof(*info));
|
||||
void GetEmulatorInfo(struct retro_system_info * info);
|
||||
GetEmulatorInfo(info);
|
||||
}
|
||||
|
||||
RETRO_API void retro_get_system_av_info(struct retro_system_av_info *info)
|
||||
{
|
||||
memset(info, 0, sizeof(*info));
|
||||
void GetEmulatorAVInfo(struct retro_system_av_info * info);
|
||||
GetEmulatorAVInfo(info);
|
||||
}
|
||||
|
||||
// RETRO_API void retro_set_controller_port_device(unsigned port, unsigned device);
|
||||
// RETRO_API void retro_reset(void);
|
||||
// RETRO_API void retro_run(void);
|
||||
// RETRO_API size_t retro_serialize_size(void);
|
||||
// RETRO_API bool retro_serialize(void *data, size_t size);
|
||||
// RETRO_API bool retro_unserialize(const void *data, size_t size);
|
||||
// RETRO_API void retro_cheat_reset(void);
|
||||
// RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code);
|
||||
// RETRO_API bool retro_load_game(const struct retro_game_info *game);
|
||||
// RETRO_API bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info);
|
||||
// RETRO_API void retro_unload_game(void);
|
||||
// RETRO_API unsigned retro_get_region(void);
|
||||
// RETRO_API void *retro_get_memory_data(unsigned id);
|
||||
// RETRO_API size_t retro_get_memory_size(unsigned id);
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 716bb5e7b761a3b4d44e069c84491d906d32aba0
|
162
libretro/libretro.c
Normal file
162
libretro/libretro.c
Normal file
|
@ -0,0 +1,162 @@
|
|||
#include "libretro.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// callbacks
|
||||
static retro_video_refresh_t retro_video_refresh = NULL;
|
||||
static retro_input_poll_t retro_input_poll = NULL;
|
||||
static retro_input_state_t retro_input_state = NULL;
|
||||
|
||||
// input params
|
||||
static unsigned input_port = 0;
|
||||
static unsigned input_device = 0;
|
||||
|
||||
RETRO_API void retro_set_environment(retro_environment_t cb)
|
||||
{
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_video_refresh(retro_video_refresh_t cb)
|
||||
{
|
||||
retro_video_refresh = cb;
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_audio_sample(retro_audio_sample_t cb)
|
||||
{
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)
|
||||
{
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_input_poll(retro_input_poll_t cb)
|
||||
{
|
||||
retro_input_poll = cb;
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_input_state(retro_input_state_t cb)
|
||||
{
|
||||
retro_input_state = cb;
|
||||
}
|
||||
|
||||
RETRO_API void retro_init(void)
|
||||
{
|
||||
void Initialize(void);
|
||||
Initialize();
|
||||
}
|
||||
|
||||
RETRO_API void retro_deinit(void)
|
||||
{
|
||||
void Deinitialize(void);
|
||||
Deinitialize();
|
||||
}
|
||||
|
||||
RETRO_API unsigned retro_api_version(void)
|
||||
{
|
||||
return RETRO_API_VERSION;
|
||||
}
|
||||
|
||||
RETRO_API void retro_get_system_info(struct retro_system_info *info)
|
||||
{
|
||||
memset(info, 0, sizeof(struct retro_system_info));
|
||||
void GetEmulatorInfo(struct retro_system_info * info);
|
||||
GetEmulatorInfo(info);
|
||||
}
|
||||
|
||||
RETRO_API void retro_get_system_av_info(struct retro_system_av_info *info)
|
||||
{
|
||||
memset(info, 0, sizeof(struct retro_system_av_info));
|
||||
void GetEmulatorAVInfo(struct retro_system_av_info * info);
|
||||
GetEmulatorAVInfo(info);
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_controller_port_device(unsigned port, unsigned device)
|
||||
{
|
||||
input_port = port;
|
||||
input_device = device;
|
||||
}
|
||||
|
||||
RETRO_API void retro_reset(void)
|
||||
{
|
||||
void Reset(void);
|
||||
Reset();
|
||||
}
|
||||
|
||||
RETRO_API void retro_run(void)
|
||||
{
|
||||
void Run(void);
|
||||
Run();
|
||||
}
|
||||
|
||||
RETRO_API size_t retro_serialize_size(void)
|
||||
{
|
||||
return 0; // TODO: implement this
|
||||
}
|
||||
|
||||
RETRO_API bool retro_serialize(void *data, size_t size)
|
||||
{
|
||||
return true; // TODO: implement this
|
||||
}
|
||||
|
||||
RETRO_API bool retro_unserialize(const void *data, size_t size)
|
||||
{
|
||||
return true; // TODO: implement this
|
||||
}
|
||||
|
||||
RETRO_API void retro_cheat_reset(void)
|
||||
{
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code)
|
||||
{
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
RETRO_API bool retro_load_game(const struct retro_game_info *game)
|
||||
{
|
||||
bool LoadGame(struct retro_game_info *);
|
||||
return LoadGame((struct retro_game_info *)game);
|
||||
}
|
||||
|
||||
RETRO_API bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info)
|
||||
{
|
||||
return true; // TODO: implement this
|
||||
}
|
||||
|
||||
RETRO_API void retro_unload_game(void)
|
||||
{
|
||||
// TODO: implement this
|
||||
}
|
||||
|
||||
RETRO_API unsigned retro_get_region(void)
|
||||
{
|
||||
return RETRO_REGION_NTSC;
|
||||
}
|
||||
|
||||
RETRO_API void *retro_get_memory_data(unsigned id)
|
||||
{
|
||||
return NULL; // TODO: implement this
|
||||
}
|
||||
|
||||
RETRO_API size_t retro_get_memory_size(unsigned id)
|
||||
{
|
||||
return 0; // TODO: implement this
|
||||
}
|
||||
|
||||
void VideoRefresh(const void *data, unsigned width, unsigned height, size_t pitch)
|
||||
{
|
||||
retro_video_refresh(data, width, height, pitch);
|
||||
}
|
||||
|
||||
void InputPoll(void)
|
||||
{
|
||||
retro_input_poll();
|
||||
}
|
||||
|
||||
int16_t InputState(unsigned id)
|
||||
{
|
||||
return retro_input_state(0, RETRO_DEVICE_JOYPAD, 0, id);
|
||||
}
|
|
@ -1,27 +1,70 @@
|
|||
package main
|
||||
|
||||
/*
|
||||
#include "libretro-common/include/libretro.h"
|
||||
#include "libretro.h"
|
||||
|
||||
typedef struct retro_system_info retro_system_info;
|
||||
typedef struct retro_system_av_info retro_system_av_info;
|
||||
typedef struct retro_game_info retro_game_info;
|
||||
|
||||
void VideoRefresh(const void *data, unsigned width, unsigned height, size_t pitch);
|
||||
void InputPoll(void);
|
||||
int16_t InputState(unsigned id);
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"image"
|
||||
"image/draw"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"unsafe"
|
||||
|
||||
"github.com/lanzafame/bobblehat/sense/screen/color"
|
||||
"github.com/tangzero/chip8-emulator/chip8"
|
||||
)
|
||||
|
||||
const (
|
||||
RetroButtonB uint8 = iota
|
||||
RetroButtonY
|
||||
RetroButtonSelect
|
||||
RetroButtonStart
|
||||
RetroButtonUp
|
||||
RetroButtonDown
|
||||
RetroButtonLeft
|
||||
RetroButtonRight
|
||||
RetroButtonA
|
||||
RetroButtonX
|
||||
RetroButtonL
|
||||
RetroButtonR
|
||||
FirstRetroButton = RetroButtonB
|
||||
LastRetroButton = RetroButtonR
|
||||
)
|
||||
|
||||
var KeyMapping = map[uint8]uint8{
|
||||
RetroButtonLeft: 0x04,
|
||||
RetroButtonX: 0x05,
|
||||
RetroButtonRight: 0x06,
|
||||
}
|
||||
|
||||
var BuildVersion string
|
||||
var emulator *chip8.Emulator
|
||||
var Emulator *chip8.Emulator
|
||||
var FrameBuffer *color.RGB565
|
||||
var KeysState [16]bool
|
||||
|
||||
//export Initialize
|
||||
func Initialize() {
|
||||
emulator = chip8.NewEmulator(nil, nil)
|
||||
playSound := func() {}
|
||||
stopSound := func() {}
|
||||
soundPlayer := func([]byte) (func(), func()) { return playSound, stopSound }
|
||||
|
||||
Emulator = chip8.NewEmulator(KeyPressed, soundPlayer)
|
||||
|
||||
FrameBuffer = color.NewRGB565(Emulator.Display.Rect)
|
||||
}
|
||||
|
||||
//export Deinitialize
|
||||
func Deinitialize() {
|
||||
emulator = nil
|
||||
Emulator = nil
|
||||
}
|
||||
|
||||
//export GetEmulatorInfo
|
||||
|
@ -29,7 +72,7 @@ func GetEmulatorInfo(info *C.retro_system_info) {
|
|||
info.library_name = C.CString("CHIP-8 Emulator by TangZero")
|
||||
info.library_version = C.CString(BuildVersion)
|
||||
info.valid_extensions = C.CString("ch8")
|
||||
info.need_fullpath = false
|
||||
info.need_fullpath = true
|
||||
info.block_extract = false
|
||||
}
|
||||
|
||||
|
@ -44,4 +87,47 @@ func GetEmulatorAVInfo(info *C.retro_system_av_info) {
|
|||
info.timing.sample_rate = 44100
|
||||
}
|
||||
|
||||
//export Reset
|
||||
func Reset() {
|
||||
Emulator.Reset()
|
||||
}
|
||||
|
||||
//export Run
|
||||
func Run() {
|
||||
C.InputPoll()
|
||||
UpdateKeysState()
|
||||
|
||||
Emulator.Update()
|
||||
|
||||
// convert from RGBA to RGB565
|
||||
draw.Draw(FrameBuffer, Emulator.Display.Rect, Emulator.Display, image.Point{}, draw.Src)
|
||||
|
||||
// draw frame
|
||||
C.VideoRefresh(unsafe.Pointer(&FrameBuffer.Pix[0]), chip8.Width, chip8.Height, C.size_t(FrameBuffer.Stride))
|
||||
}
|
||||
|
||||
//export LoadGame
|
||||
func LoadGame(game *C.retro_game_info) bool {
|
||||
data, err := ioutil.ReadFile(C.GoString(game.path))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return false
|
||||
}
|
||||
Emulator.LoadROM(chip8.ROM{Data: data})
|
||||
return true
|
||||
}
|
||||
|
||||
func UpdateKeysState() {
|
||||
for button := FirstRetroButton; button < LastRetroButton; button++ {
|
||||
key, ok := KeyMapping[button]
|
||||
if ok {
|
||||
KeysState[key] = C.InputState(C.uint(button)) > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func KeyPressed(key uint8) bool {
|
||||
return KeysState[key]
|
||||
}
|
||||
|
||||
func main() {}
|
||||
|
|
3890
libretro/libretro.h
Normal file
3890
libretro/libretro.h
Normal file
File diff suppressed because it is too large
Load Diff
16
main.go
16
main.go
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
_ "embed"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/audio"
|
||||
|
@ -57,24 +56,11 @@ type GUI struct {
|
|||
Emulator *chip8.Emulator
|
||||
}
|
||||
|
||||
func (gui *GUI) Run() {
|
||||
for {
|
||||
gui.Emulator.Cycle()
|
||||
time.Sleep(time.Millisecond * 2)
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *GUI) Update() error {
|
||||
if ebiten.IsKeyPressed(ebiten.KeyEscape) {
|
||||
gui.Emulator.Reset()
|
||||
}
|
||||
switch gui.State {
|
||||
case LoadingState:
|
||||
go gui.Run()
|
||||
gui.State = RunningState
|
||||
case RunningState:
|
||||
gui.Emulator.UpdateTimers()
|
||||
}
|
||||
gui.Emulator.Update()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user