commit
db7e0625bd
17 changed files with 858 additions and 19 deletions
|
@ -12,6 +12,9 @@ MESSAGE("CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}")
|
|||
MESSAGE("CMAKE_CXX_FLAGS_DEBUG: ${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
MESSAGE("CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
||||
|
||||
# this only affects common2, the others are already build with fPIC by default
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
execute_process(COMMAND uname -n
|
||||
|
@ -35,4 +38,5 @@ add_subdirectory(source/frontends/common2)
|
|||
add_subdirectory(source/frontends/ncurses)
|
||||
add_subdirectory(source/frontends/qapple)
|
||||
add_subdirectory(source/frontends/sa2)
|
||||
add_subdirectory(source/frontends/retro)
|
||||
add_subdirectory(test/TestCPU6502)
|
||||
|
|
15
linux.md
15
linux.md
|
@ -6,6 +6,7 @@
|
|||
* [applen](#applen)
|
||||
* [qapple](#qapple)
|
||||
* [sa2](#sa2)
|
||||
* [libretro](#ra2)
|
||||
* [Build](#build)
|
||||
* [Checkout](#checkout)
|
||||
* [Fedora](#fedora)
|
||||
|
@ -88,6 +89,20 @@ This is based on Qt, currently tested with 5.10
|
|||
|
||||
See [sa2](source/frontends/sa2/README.md).
|
||||
|
||||
### ra2
|
||||
|
||||
There is an initial [libretro](https://docs.libretro.com/development/cores/developing-cores/) core.
|
||||
|
||||
Keyboard works, but a lot of keys overlap with RetroArch shortcuts.
|
||||
|
||||
Video works, but the vertical flip is done in software.
|
||||
|
||||
Must be manually configured:
|
||||
``cmake -DLIBRETRO_PATH=/path/to/libretro-common``
|
||||
|
||||
Easiest way to run from the ``build`` folder:
|
||||
``retroarch -L source/frontends/retro/libra2.so ../Disks/NoSlotClockTest.dsk``
|
||||
|
||||
## Build
|
||||
|
||||
The project can be built using cmake from the top level directory.
|
||||
|
|
|
@ -18,7 +18,7 @@ target_include_directories(common2 PRIVATE
|
|||
${Boost_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(common2 PRIVATE
|
||||
target_link_libraries(common2 PUBLIC
|
||||
Boost::program_options
|
||||
)
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// relative path from executable to resources
|
||||
#cmakedefine ROOT_PATH "@ROOT_PATH@"
|
||||
#cmakedefine SHARE_PATH "@SHARE_PATH@"
|
||||
|
||||
// this one is a bit of a hack, until resources are embedded in the retro core
|
||||
#cmakedefine CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@"
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace
|
|||
|
||||
std::string getResourcePathImpl()
|
||||
{
|
||||
std::vector<std::string> paths;
|
||||
|
||||
char self[1024] = {0};
|
||||
const int ch = readlink("/proc/self/exe", self, sizeof(self));
|
||||
if (ch != -1)
|
||||
|
@ -34,21 +36,24 @@ namespace
|
|||
const char * path = dirname(self);
|
||||
|
||||
// case 1: run from the build folder
|
||||
const std::string path1 = std::string(path) + '/'+ ROOT_PATH + "/resource/";
|
||||
if (dirExists(path1))
|
||||
{
|
||||
return path1;
|
||||
}
|
||||
|
||||
paths.emplace_back(std::string(path) + '/'+ ROOT_PATH);
|
||||
// case 2: run from the installation folder
|
||||
const std::string path2 = std::string(path) + '/'+ SHARE_PATH + "/resource/";
|
||||
if (dirExists(path2))
|
||||
paths.emplace_back(std::string(path) + '/'+ SHARE_PATH);
|
||||
}
|
||||
|
||||
// case 3: use the source folder
|
||||
paths.emplace_back(CMAKE_SOURCE_DIR);
|
||||
|
||||
for (const std::string & path : paths)
|
||||
{
|
||||
const std::string resourcePath = path + "/resource/";
|
||||
if (dirExists(resourcePath))
|
||||
{
|
||||
return path2;
|
||||
return resourcePath;
|
||||
}
|
||||
}
|
||||
// else?
|
||||
return std::string();
|
||||
|
||||
throw std::runtime_error("Cannot found the resource path");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
27
source/frontends/retro/CMakeLists.txt
Normal file
27
source/frontends/retro/CMakeLists.txt
Normal file
|
@ -0,0 +1,27 @@
|
|||
# there is no simple package for libretro-common
|
||||
# this requires user input
|
||||
set(LIBRETRO_PATH NONE CACHE PATH "path to libretro-common")
|
||||
|
||||
if (IS_DIRECTORY ${LIBRETRO_PATH})
|
||||
message("Using LIBRETRO_PATH=${LIBRETRO_PATH}")
|
||||
|
||||
add_library(ra2 SHARED
|
||||
environment.cpp
|
||||
libretro.cpp
|
||||
bitmaps.cpp
|
||||
rdirectsound.cpp
|
||||
interface.cpp
|
||||
game.cpp
|
||||
)
|
||||
|
||||
target_include_directories(ra2 PRIVATE
|
||||
${LIBRETRO_PATH}/include
|
||||
)
|
||||
|
||||
target_link_libraries(ra2 PRIVATE
|
||||
appleii
|
||||
common2
|
||||
)
|
||||
else()
|
||||
message("Bad LIBRETRO_PATH=${LIBRETRO_PATH}, skipping libretro code")
|
||||
endif()
|
151
source/frontends/retro/bitmaps.cpp
Normal file
151
source/frontends/retro/bitmaps.cpp
Normal file
|
@ -0,0 +1,151 @@
|
|||
#include "frontends/retro/environment.h"
|
||||
|
||||
#include <linux/interface.h>
|
||||
#include <linux/windows/misc.h>
|
||||
#include <frontends/common2/resources.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void readFileToBuffer(const std::string & filename, std::vector<char> & buffer)
|
||||
{
|
||||
std::ifstream file(filename.c_str(), std::ios::binary | std::ios::ate);
|
||||
const std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
buffer.resize(size);
|
||||
file.read(buffer.data(), size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T getAs(const std::vector<char> & buffer, const size_t offset)
|
||||
{
|
||||
if (offset + sizeof(T) > buffer.size())
|
||||
{
|
||||
throw std::runtime_error("Invalid bitmap");
|
||||
}
|
||||
const T * ptr = reinterpret_cast<const T *>(buffer.data() + offset);
|
||||
return * ptr;
|
||||
}
|
||||
|
||||
// libretro cannot parse BMP with 1 bpp
|
||||
// simple version implemented here
|
||||
bool getBitmapData(const std::vector<char> & buffer, int32_t & width, int32_t & height, uint16_t & bpp, const char * & data, uint32_t & size)
|
||||
{
|
||||
if (buffer.size() < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buffer[0] != 0x42 || buffer[1] != 0x4D)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t fileSize = getAs<uint32_t>(buffer, 2);
|
||||
if (fileSize != buffer.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t offset = getAs<uint32_t>(buffer, 10);
|
||||
const uint32_t header = getAs<uint32_t>(buffer, 14);
|
||||
if (header != 40)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
width = getAs<int32_t>(buffer, 18);
|
||||
height = getAs<int32_t>(buffer, 22);
|
||||
bpp = getAs<uint16_t>(buffer, 28);
|
||||
const uint32_t imageSize = getAs<uint32_t>(buffer, 34);
|
||||
if (offset + imageSize > fileSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
data = buffer.data() + offset;
|
||||
size = imageSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct CBITMAP : public CHANDLE
|
||||
{
|
||||
std::vector<char> image;
|
||||
};
|
||||
|
||||
std::string getFilename(const std::string & resource)
|
||||
{
|
||||
if (resource == "CHARSET40") return "CHARSET4.BMP";
|
||||
if (resource == "CHARSET82") return "CHARSET82.bmp";
|
||||
if (resource == "CHARSET8M") return "CHARSET8M.bmp";
|
||||
if (resource == "CHARSET8C") return "CHARSET8C.bmp";
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
HBITMAP LoadBitmap(HINSTANCE hInstance, const char * resource)
|
||||
{
|
||||
if (resource)
|
||||
{
|
||||
const std::string filename = getFilename(resource);
|
||||
const std::string path = getResourcePath() + filename;
|
||||
|
||||
std::vector<char> buffer;
|
||||
readFileToBuffer(path, buffer);
|
||||
|
||||
if (!buffer.empty())
|
||||
{
|
||||
int32_t width, height;
|
||||
uint16_t bpp;
|
||||
const char * data;
|
||||
uint32_t size;
|
||||
const bool res = getBitmapData(buffer, width, height, bpp, data, size);
|
||||
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s. %s = %dx%d, %dbpp\n", __FUNCTION__, path.c_str(),
|
||||
width, height, bpp);
|
||||
|
||||
if (res && height > 0)
|
||||
{
|
||||
CBITMAP * bitmap = new CBITMAP;
|
||||
bitmap->image.resize(size);
|
||||
const size_t length = size / height;
|
||||
// rows are stored upside down
|
||||
for (size_t row = 0; row < height; ++row)
|
||||
{
|
||||
const char * src = data + row * length;
|
||||
char * dst = bitmap->image.data() + (height - row - 1) * length;
|
||||
memcpy(dst, src, length);
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s. Cannot load: %s with path: %s\n", __FUNCTION__, resource, path.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s. Cannot load invaid resource\n", __FUNCTION__);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LONG GetBitmapBits(HBITMAP hbit, LONG cb, LPVOID lpvBits)
|
||||
{
|
||||
const CBITMAP & bitmap = dynamic_cast<CBITMAP&>(*hbit);
|
||||
const char * bits = bitmap.image.data();
|
||||
const size_t size = bitmap.image.size();
|
||||
const size_t requested = cb;
|
||||
|
||||
const size_t copied = std::min(requested, size);
|
||||
|
||||
char * dest = static_cast<char *>(lpvBits);
|
||||
memcpy(dest, bits, copied);
|
||||
|
||||
return copied;
|
||||
}
|
28
source/frontends/retro/environment.cpp
Normal file
28
source/frontends/retro/environment.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "frontends/retro/environment.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
void fallback_log(enum retro_log_level level, const char *fmt, ...)
|
||||
{
|
||||
(void)level;
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
vfprintf(stderr, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
retro_log_callback logging;
|
||||
retro_log_printf_t log_cb = fallback_log;
|
||||
retro_input_poll_t input_poll_cb;
|
||||
retro_input_state_t input_state_cb;
|
||||
|
||||
retro_environment_t environ_cb;
|
||||
retro_video_refresh_t video_cb;
|
||||
retro_audio_sample_t audio_cb;
|
||||
retro_audio_sample_batch_t audio_batch_cb;
|
||||
|
||||
std::string retro_base_directory;
|
||||
|
||||
unsigned int retro_devices[RETRO_DEVICES] = {};
|
19
source/frontends/retro/environment.h
Normal file
19
source/frontends/retro/environment.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#include "libretro.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
void fallback_log(enum retro_log_level level, const char *fmt, ...);
|
||||
extern retro_log_callback logging;
|
||||
extern retro_log_printf_t log_cb;
|
||||
extern retro_input_poll_t input_poll_cb;
|
||||
extern retro_input_state_t input_state_cb;
|
||||
extern retro_environment_t environ_cb;
|
||||
extern retro_video_refresh_t video_cb;
|
||||
extern retro_audio_sample_t audio_cb;
|
||||
extern retro_audio_sample_batch_t audio_batch_cb;
|
||||
|
||||
extern std::string retro_base_directory;
|
||||
|
||||
#define RETRO_DEVICES 5
|
||||
|
||||
extern unsigned int retro_devices[RETRO_DEVICES];
|
297
source/frontends/retro/game.cpp
Normal file
297
source/frontends/retro/game.cpp
Normal file
|
@ -0,0 +1,297 @@
|
|||
#include "StdAfx.h"
|
||||
#include "frontends/retro/game.h"
|
||||
|
||||
#include "Frame.h"
|
||||
#include "Video.h"
|
||||
|
||||
#include "Common.h"
|
||||
#include "CardManager.h"
|
||||
#include "Core.h"
|
||||
#include "Disk.h"
|
||||
#include "Mockingboard.h"
|
||||
#include "SoundCore.h"
|
||||
#include "Harddisk.h"
|
||||
#include "Speaker.h"
|
||||
#include "Log.h"
|
||||
#include "CPU.h"
|
||||
#include "Memory.h"
|
||||
#include "LanguageCard.h"
|
||||
#include "MouseInterface.h"
|
||||
#include "ParallelPrinter.h"
|
||||
#include "NTSC.h"
|
||||
#include "SaveState.h"
|
||||
#include "RGBMonitor.h"
|
||||
#include "Riff.h"
|
||||
#include "Utilities.h"
|
||||
|
||||
#include "linux/videobuffer.h"
|
||||
#include "linux/keyboard.h"
|
||||
#include "linux/paddle.h"
|
||||
#include "frontends/common2/programoptions.h"
|
||||
#include "frontends/common2/configuration.h"
|
||||
#include "frontends/retro/environment.h"
|
||||
|
||||
#include "libretro.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void initialiseEmulator()
|
||||
{
|
||||
#ifdef RIFF_SPKR
|
||||
RiffInitWriteFile("/tmp/Spkr.wav", SPKR_SAMPLE_RATE, 1);
|
||||
#endif
|
||||
#ifdef RIFF_MB
|
||||
RiffInitWriteFile("/tmp/Mockingboard.wav", 44100, 2);
|
||||
#endif
|
||||
|
||||
g_nAppMode = MODE_RUNNING;
|
||||
LogFileOutput("Initialisation\n");
|
||||
|
||||
g_bFullSpeed = false;
|
||||
|
||||
LoadConfiguration();
|
||||
SetCurrentCLK6502();
|
||||
GetAppleWindowTitle();
|
||||
|
||||
DSInit();
|
||||
MB_Initialize();
|
||||
SpkrInitialize();
|
||||
|
||||
MemInitialize();
|
||||
VideoBufferInitialize();
|
||||
VideoSwitchVideocardPalette(RGB_GetVideocard(), GetVideoType());
|
||||
|
||||
GetCardMgr().GetDisk2CardMgr().Reset();
|
||||
HD_Reset();
|
||||
Snapshot_Startup();
|
||||
}
|
||||
|
||||
void uninitialiseEmulator()
|
||||
{
|
||||
Snapshot_Shutdown();
|
||||
CMouseInterface* pMouseCard = GetCardMgr().GetMouseCard();
|
||||
if (pMouseCard)
|
||||
{
|
||||
pMouseCard->Reset();
|
||||
}
|
||||
VideoBufferDestroy();
|
||||
MemDestroy();
|
||||
|
||||
SpkrDestroy();
|
||||
MB_Destroy();
|
||||
DSUninit();
|
||||
|
||||
HD_Destroy();
|
||||
PrintDestroy();
|
||||
CpuDestroy();
|
||||
|
||||
GetCardMgr().GetDisk2CardMgr().Destroy();
|
||||
LogDone();
|
||||
RiffFinishWriteFile();
|
||||
}
|
||||
|
||||
void runOneFrame(Speed & speed)
|
||||
{
|
||||
if (g_nAppMode == MODE_RUNNING)
|
||||
{
|
||||
const size_t cyclesToExecute = speed.getCyclesTillNext(16);
|
||||
|
||||
const bool bVideoUpdate = true;
|
||||
const UINT dwClksPerFrame = NTSC_GetCyclesPerFrame();
|
||||
|
||||
const DWORD executedCycles = CpuExecute(cyclesToExecute, bVideoUpdate);
|
||||
// log_cb(RETRO_LOG_INFO, "RA2: %s %d\n", __FUNCTION__, executedCycles);
|
||||
|
||||
g_dwCyclesThisFrame = (g_dwCyclesThisFrame + executedCycles) % dwClksPerFrame;
|
||||
GetCardMgr().GetDisk2CardMgr().UpdateDriveState(executedCycles);
|
||||
MB_PeriodicUpdate(executedCycles);
|
||||
SpkrUpdate(executedCycles);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Game::Game()
|
||||
: mySpeed(true)
|
||||
, myWidth(GetFrameBufferWidth())
|
||||
, myHeight(GetFrameBufferHeight())
|
||||
{
|
||||
EmulatorOptions options;
|
||||
options.memclear = g_nMemoryClearType;
|
||||
options.log = true;
|
||||
|
||||
if (options.log)
|
||||
{
|
||||
LogInit();
|
||||
}
|
||||
|
||||
InitializeRegistry(options);
|
||||
initialiseEmulator();
|
||||
|
||||
const size_t size = myWidth * myHeight * sizeof(bgra_t);
|
||||
myVideoBuffer.resize(size);
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
uninitialiseEmulator();
|
||||
}
|
||||
|
||||
void Game::executeOneFrame()
|
||||
{
|
||||
runOneFrame(mySpeed);
|
||||
}
|
||||
|
||||
void Game::processInputEvents()
|
||||
{
|
||||
input_poll_cb();
|
||||
}
|
||||
|
||||
void Game::keyboardCallback(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers)
|
||||
{
|
||||
if (down)
|
||||
{
|
||||
processKeyDown(keycode, character, key_modifiers);
|
||||
}
|
||||
else
|
||||
{
|
||||
processKeyUp(keycode, character, key_modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
void Game::processKeyDown(unsigned keycode, uint32_t character, uint16_t key_modifiers)
|
||||
{
|
||||
BYTE ch = 0;
|
||||
switch (keycode)
|
||||
{
|
||||
case RETROK_RETURN:
|
||||
{
|
||||
ch = 0x0d;
|
||||
break;
|
||||
}
|
||||
case RETROK_BACKSPACE: // same as AppleWin
|
||||
case RETROK_LEFT:
|
||||
{
|
||||
ch = 0x08;
|
||||
break;
|
||||
}
|
||||
case RETROK_RIGHT:
|
||||
{
|
||||
ch = 0x15;
|
||||
break;
|
||||
}
|
||||
case RETROK_UP:
|
||||
{
|
||||
ch = 0x0b;
|
||||
break;
|
||||
}
|
||||
case RETROK_DOWN:
|
||||
{
|
||||
ch = 0x0a;
|
||||
break;
|
||||
}
|
||||
case RETROK_DELETE:
|
||||
{
|
||||
ch = 0x7f;
|
||||
break;
|
||||
}
|
||||
case RETROK_ESCAPE:
|
||||
{
|
||||
ch = 0x1b;
|
||||
break;
|
||||
}
|
||||
case RETROK_TAB:
|
||||
{
|
||||
ch = 0x09;
|
||||
break;
|
||||
}
|
||||
case RETROK_LALT:
|
||||
{
|
||||
Paddle::setButtonPressed(Paddle::ourOpenApple);
|
||||
break;
|
||||
}
|
||||
case RETROK_RALT:
|
||||
{
|
||||
Paddle::setButtonPressed(Paddle::ourSolidApple);
|
||||
break;
|
||||
}
|
||||
case RETROK_a ... RETROK_z:
|
||||
{
|
||||
ch = (keycode - RETROK_a) + 0x01;
|
||||
if (key_modifiers & RETROKMOD_CTRL)
|
||||
{
|
||||
// ok
|
||||
}
|
||||
else if (key_modifiers & RETROKMOD_SHIFT)
|
||||
{
|
||||
ch += 0x60;
|
||||
}
|
||||
else
|
||||
{
|
||||
ch += 0x40;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ch)
|
||||
{
|
||||
switch (character) {
|
||||
case 0x20 ... 0x40:
|
||||
case 0x5b ... 0x60:
|
||||
case 0x7b ... 0x7e:
|
||||
{
|
||||
// not the letters
|
||||
// this is very simple, but one cannot handle CRTL-key combination.
|
||||
ch = character;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ch)
|
||||
{
|
||||
addKeyToBuffer(ch);
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s - %02x\n", __FUNCTION__, ch);
|
||||
}
|
||||
}
|
||||
|
||||
void Game::processKeyUp(unsigned keycode, uint32_t character, uint16_t key_modifiers)
|
||||
{
|
||||
switch (keycode)
|
||||
{
|
||||
case RETROK_LALT:
|
||||
{
|
||||
Paddle::setButtonReleased(Paddle::ourOpenApple);
|
||||
break;
|
||||
}
|
||||
case RETROK_RALT:
|
||||
{
|
||||
Paddle::setButtonReleased(Paddle::ourSolidApple);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Game::drawVideoBuffer()
|
||||
{
|
||||
const size_t pitch = myWidth * sizeof(bgra_t);
|
||||
// this should not be necessary
|
||||
// either libretro handles it
|
||||
// or we should change AW
|
||||
// but for now, there is no alternative
|
||||
for (size_t row = 0; row < myHeight; ++row)
|
||||
{
|
||||
const uint8_t * src = g_pFramebufferbits + row * pitch;
|
||||
uint8_t * dst = myVideoBuffer.data() + (myHeight - row - 1) * pitch;
|
||||
memcpy(dst, src, pitch);
|
||||
}
|
||||
video_cb(myVideoBuffer.data(), myWidth, myHeight, pitch);
|
||||
}
|
||||
|
||||
bool Game::loadGame(const char * path)
|
||||
{
|
||||
const bool ok = DoDiskInsert(SLOT6, DRIVE_1, path);
|
||||
return ok;
|
||||
}
|
30
source/frontends/retro/game.h
Normal file
30
source/frontends/retro/game.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "frontends/common2/speed.h"
|
||||
|
||||
class Game
|
||||
{
|
||||
public:
|
||||
Game();
|
||||
~Game();
|
||||
|
||||
bool loadGame(const char * path);
|
||||
|
||||
void executeOneFrame();
|
||||
void processInputEvents();
|
||||
|
||||
void drawVideoBuffer();
|
||||
|
||||
static void keyboardCallback(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers);
|
||||
|
||||
private:
|
||||
Speed mySpeed; // fixed speed
|
||||
|
||||
const size_t myHeight;
|
||||
const size_t myWidth;
|
||||
|
||||
static void processKeyDown(unsigned keycode, uint32_t character, uint16_t key_modifiers);
|
||||
static void processKeyUp(unsigned keycode, uint32_t character, uint16_t key_modifiers);
|
||||
|
||||
std::vector<uint8_t> myVideoBuffer;
|
||||
};
|
22
source/frontends/retro/interface.cpp
Normal file
22
source/frontends/retro/interface.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include "linux/interface.h"
|
||||
|
||||
#include "frontends/retro/environment.h"
|
||||
#include "linux/win.h"
|
||||
|
||||
int MessageBox(HWND, const char * text, const char * caption, UINT type)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s: %s - %s\n", __FUNCTION__, caption, text);
|
||||
return IDOK;
|
||||
}
|
||||
|
||||
void FrameDrawDiskLEDS(HDC x)
|
||||
{
|
||||
}
|
||||
|
||||
void FrameDrawDiskStatus(HDC x)
|
||||
{
|
||||
}
|
||||
|
||||
void FrameRefreshStatus(int x, bool)
|
||||
{
|
||||
}
|
231
source/frontends/retro/libretro.cpp
Normal file
231
source/frontends/retro/libretro.cpp
Normal file
|
@ -0,0 +1,231 @@
|
|||
#include "libretro.h"
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "Frame.h"
|
||||
|
||||
#include "linux/version.h"
|
||||
|
||||
#include "frontends/retro/game.h"
|
||||
#include "frontends/retro/environment.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
std::unique_ptr<Game> game;
|
||||
}
|
||||
|
||||
void retro_init(void)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
const char *dir = NULL;
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir)
|
||||
{
|
||||
retro_base_directory = dir;
|
||||
}
|
||||
}
|
||||
|
||||
void retro_deinit(void)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
unsigned retro_api_version(void)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
return RETRO_API_VERSION;
|
||||
}
|
||||
|
||||
void retro_set_controller_port_device(unsigned port, unsigned device)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
log_cb(RETRO_LOG_INFO, "Plugging device %u into port %u.\n", device, port);
|
||||
|
||||
if (port < RETRO_DEVICES)
|
||||
retro_devices[port] = device;
|
||||
}
|
||||
|
||||
void retro_get_system_info(retro_system_info *info)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
static std::string version = getVersion();
|
||||
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->library_name = "AppleWin";
|
||||
info->library_version = version.c_str();
|
||||
info->need_fullpath = true;
|
||||
info->valid_extensions = "bin|do|dsk|nib|po|gz|woz|zip|2mg|2img|iie|apl|hdv";
|
||||
}
|
||||
|
||||
|
||||
void retro_get_system_av_info(retro_system_av_info *info)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
|
||||
info->geometry.base_width = GetFrameBufferWidth();
|
||||
info->geometry.base_height = GetFrameBufferHeight();
|
||||
info->geometry.max_width = GetFrameBufferWidth();
|
||||
info->geometry.max_height = GetFrameBufferHeight();
|
||||
info->geometry.aspect_ratio = 0;
|
||||
|
||||
info->timing.fps = 60;
|
||||
info->timing.sample_rate = 0;
|
||||
}
|
||||
|
||||
void retro_set_environment(retro_environment_t cb)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
environ_cb = cb;
|
||||
|
||||
if (cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &logging))
|
||||
log_cb = logging.log;
|
||||
else
|
||||
log_cb = fallback_log;
|
||||
/*
|
||||
static const retro_controller_description controllers[] =
|
||||
{
|
||||
{ "Apple Keyboard", RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_KEYBOARD, 0) },
|
||||
};
|
||||
|
||||
static const retro_controller_info ports[] =
|
||||
{
|
||||
{ controllers, 1 },
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
||||
cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);*/
|
||||
retro_keyboard_callback callback = {&Game::keyboardCallback};
|
||||
cb(RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK, &callback);
|
||||
}
|
||||
|
||||
void retro_set_audio_sample(retro_audio_sample_t cb)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
audio_cb = cb;
|
||||
}
|
||||
|
||||
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
audio_batch_cb = cb;
|
||||
}
|
||||
|
||||
void retro_set_input_poll(retro_input_poll_t cb)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
input_poll_cb = cb;
|
||||
}
|
||||
|
||||
void retro_set_input_state(retro_input_state_t cb)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
input_state_cb = cb;
|
||||
}
|
||||
|
||||
void retro_set_video_refresh(retro_video_refresh_t cb)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
video_cb = cb;
|
||||
}
|
||||
|
||||
void retro_run(void)
|
||||
{
|
||||
game->processInputEvents();
|
||||
game->executeOneFrame();
|
||||
game->drawVideoBuffer();
|
||||
}
|
||||
|
||||
bool retro_load_game(const retro_game_info *info)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
|
||||
enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;
|
||||
if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "XRGB8888 is not supported.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
game.reset(new Game);
|
||||
const bool ok = game->loadGame(info->path);
|
||||
|
||||
log_cb(RETRO_LOG_INFO, "Game path: %s:%d\n", info->path, ok);
|
||||
|
||||
return ok;
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "Exception: %s\n", e.what());
|
||||
}
|
||||
catch (const std::string & s)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "Exception: %s\n", s.c_str());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void retro_unload_game(void)
|
||||
{
|
||||
game.reset();
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
unsigned retro_get_region(void)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
return RETRO_REGION_NTSC;
|
||||
}
|
||||
|
||||
void retro_reset(void)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t num)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t retro_serialize_size(void)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool retro_serialize(void *data, size_t size)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool retro_unserialize(const void *data, size_t size)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
void retro_cheat_reset(void)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
void retro_cheat_set(unsigned index, bool enabled, const char *code)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
void *retro_get_memory_data(unsigned id)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t retro_get_memory_size(unsigned id)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
10
source/frontends/retro/rdirectsound.cpp
Normal file
10
source/frontends/retro/rdirectsound.cpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include <linux/interface.h>
|
||||
|
||||
// Mockingboard
|
||||
void registerSoundBuffer(IDirectSoundBuffer * buffer)
|
||||
{
|
||||
}
|
||||
|
||||
void unregisterSoundBuffer(IDirectSoundBuffer * buffer)
|
||||
{
|
||||
}
|
1
source/frontends/retro/run
Normal file
1
source/frontends/retro/run
Normal file
|
@ -0,0 +1 @@
|
|||
retroarch -L source/frontends/retro/libra2.so ../Disks/NoSlotClockTest.dsk
|
|
@ -9,23 +9,17 @@ add_executable(sa2
|
|||
utils.cpp
|
||||
)
|
||||
|
||||
find_package(Boost REQUIRED
|
||||
COMPONENTS program_options
|
||||
)
|
||||
|
||||
find_package(SDL2 REQUIRED)
|
||||
pkg_search_module(SDL2_IMAGE REQUIRED SDL2_image)
|
||||
|
||||
target_compile_features(sa2 PUBLIC cxx_std_17)
|
||||
|
||||
target_include_directories(sa2 PRIVATE
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${SDL2_INCLUDE_DIRS}
|
||||
${SDL2_IMAGE_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(sa2 PRIVATE
|
||||
Boost::program_options
|
||||
${SDL2_LIBRARIES}
|
||||
${SDL2_IMAGE_LIBRARIES}
|
||||
appleii
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
set -euxo pipefail
|
||||
|
||||
wget https://raw.githubusercontent.com/libretro/RetroArch/master/libretro-common/include/libretro.h -P build/libretro-common/include
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=RELEASE ..
|
||||
cmake -DCMAKE_BUILD_TYPE=RELEASE -DLIBRETRO_PATH=libretro-common ..
|
||||
make
|
||||
|
|
Loading…
Add table
Reference in a new issue