very basic libretro core (no sound + limited controls)

This commit is contained in:
Jairinho 2022-03-02 10:34:08 -03:00
parent e6b150136c
commit 529672f874
No known key found for this signature in database
GPG Key ID: 954589B18A21D5B6
12 changed files with 4168 additions and 84 deletions

3
.gitmodules vendored
View File

@ -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
View File

@ -0,0 +1,5 @@
{
"files.associations": {
"libretro.h": "c"
}
}

View File

@ -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)

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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
View 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);
}

View File

@ -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

File diff suppressed because it is too large Load Diff

16
main.go
View File

@ -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
}