From c176b3d2ffbff5f289b9e5a1f0375046f8f9338e Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Fri, 11 Dec 2020 08:16:12 +0000 Subject: [PATCH 01/15] Add skeleton of a libretro core for AW. Signed-off-by: Andrea Odetti --- CMakeLists.txt | 4 + source/frontends/common2/CMakeLists.txt | 2 +- source/frontends/common2/resources.cpp | 2 +- source/frontends/retro/CMakeLists.txt | 17 ++ source/frontends/retro/bitmaps.cpp | 143 +++++++++ source/frontends/retro/emulator.cpp | 384 ++++++++++++++++++++++++ source/frontends/retro/emulator.h | 41 +++ source/frontends/retro/environment.cpp | 17 ++ source/frontends/retro/environment.h | 5 + source/frontends/retro/libretro.cpp | 365 ++++++++++++++++++++++ source/frontends/retro/rdirectsound.cpp | 10 + source/frontends/sa2/CMakeLists.txt | 6 - 12 files changed, 988 insertions(+), 8 deletions(-) create mode 100644 source/frontends/retro/CMakeLists.txt create mode 100644 source/frontends/retro/bitmaps.cpp create mode 100644 source/frontends/retro/emulator.cpp create mode 100644 source/frontends/retro/emulator.h create mode 100644 source/frontends/retro/environment.cpp create mode 100644 source/frontends/retro/environment.h create mode 100644 source/frontends/retro/libretro.cpp create mode 100644 source/frontends/retro/rdirectsound.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 442bb30a..b464b8ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/source/frontends/common2/CMakeLists.txt b/source/frontends/common2/CMakeLists.txt index 434177cb..5f705e16 100644 --- a/source/frontends/common2/CMakeLists.txt +++ b/source/frontends/common2/CMakeLists.txt @@ -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 ) diff --git a/source/frontends/common2/resources.cpp b/source/frontends/common2/resources.cpp index a2b7699c..012d6870 100644 --- a/source/frontends/common2/resources.cpp +++ b/source/frontends/common2/resources.cpp @@ -48,7 +48,7 @@ namespace } } // else? - return std::string(); + return "/home/andrea/projects/cvs/A2E/resource/"; } } diff --git a/source/frontends/retro/CMakeLists.txt b/source/frontends/retro/CMakeLists.txt new file mode 100644 index 00000000..963c7ce6 --- /dev/null +++ b/source/frontends/retro/CMakeLists.txt @@ -0,0 +1,17 @@ +add_library(ra2 SHARED + ~/projects/games/retroarch/libretro-super/retroarch/libretro-common/formats/bmp/rbmp.c + environment.cpp + libretro.cpp + bitmaps.cpp + rdirectsound.cpp + ) + +target_include_directories(ra2 PRIVATE + ~/projects/games/retroarch/libretro-super/retroarch/libretro-common + ~/projects/games/retroarch/libretro-super/retroarch/libretro-common/include + ) + +target_link_libraries(ra2 PRIVATE + appleii + common2 + ) diff --git a/source/frontends/retro/bitmaps.cpp b/source/frontends/retro/bitmaps.cpp new file mode 100644 index 00000000..0de23574 --- /dev/null +++ b/source/frontends/retro/bitmaps.cpp @@ -0,0 +1,143 @@ +#include "frontends/retro/environment.h" + +#include +#include +#include + +#include + +namespace +{ + + void readFileToBuffer(const std::string & filename, std::vector & 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 + T getAs(const std::vector & buffer, const size_t offset) + { + if (offset + sizeof(T) > buffer.size()) + { + throw std::runtime_error("Invalid bitmap"); + } + const T * ptr = reinterpret_cast(buffer.data() + offset); + return * ptr; + } + + // libretro cannot parse BMP with 1 bpp + // simple version implemented here + bool getBitmapData(const std::vector & 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(buffer, 2); + if (fileSize != buffer.size()) + { + return false; + } + + const uint32_t offset = getAs(buffer, 10); + const uint32_t header = getAs(buffer, 14); + if (header != 40) + { + return false; + } + + width = getAs(buffer, 18); + height = getAs(buffer, 22); + bpp = getAs(buffer, 28); + const uint32_t imageSize = getAs(buffer, 34); + if (offset + imageSize > fileSize) + { + return false; + } + data = buffer.data() + offset; + size = imageSize; + return true; + } + +} + +struct CBITMAP : public CHANDLE +{ + std::vector 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; + + log_cb(RETRO_LOG_INFO, "RA2: %s. Path = %s\n", __FUNCTION__, path.c_str()); + + std::vector 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); + + if (res) + { + CBITMAP * bitmap = new CBITMAP; + bitmap->image.assign(data, data + size); + 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(*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(lpvBits); + for (size_t i = 0; i < copied; ++i) + { + dest[i] = ~bits[i]; + } + return copied; +} diff --git a/source/frontends/retro/emulator.cpp b/source/frontends/retro/emulator.cpp new file mode 100644 index 00000000..e518bebf --- /dev/null +++ b/source/frontends/retro/emulator.cpp @@ -0,0 +1,384 @@ +#include "frontends/sa2/emulator.h" +#include "frontends/sa2/sdirectsound.h" +#include "frontends/sa2/utils.h" + +#include + +#include "linux/videobuffer.h" +#include "linux/paddle.h" +#include "linux/keyboard.h" + +#include "StdAfx.h" +#include "Common.h" +#include "CardManager.h" +#include "Core.h" +#include "Disk.h" +#include "CPU.h" +#include "Frame.h" +#include "Video.h" +#include "Windows/WinVideo.h" +#include "NTSC.h" +#include "Mockingboard.h" +#include "Speaker.h" +#include "Utilities.h" +#include "SaveState.h" +#include "SoundCore.h" + +// #define KEY_LOGGING_VERBOSE + +namespace +{ + + void updateWindowTitle(const std::shared_ptr & win) + { + GetAppleWindowTitle(); + SDL_SetWindowTitle(win.get(), g_pAppTitle.c_str()); + } + + void cycleVideoType(const std::shared_ptr & win) + { + g_eVideoType++; + if (g_eVideoType >= NUM_VIDEO_MODES) + g_eVideoType = 0; + + Config_Save_Video(); + VideoReinitialize(); + VideoRedrawScreen(); + + updateWindowTitle(win); + } + + void cycle50ScanLines(const std::shared_ptr & win) + { + VideoStyle_e videoStyle = GetVideoStyle(); + videoStyle = VideoStyle_e(videoStyle ^ VS_HALF_SCANLINES); + + SetVideoStyle(videoStyle); + + Config_Save_Video(); + VideoReinitialize(); + VideoRedrawScreen(); + + updateWindowTitle(win); + } + + void processAppleKey(const SDL_KeyboardEvent & key) + { + // using keycode (or scan code) one takes a physical view of the keyboard + // ignoring non US layouts + // SHIFT-3 on a A2 keyboard in # while on my UK keyboard is £? + // so for now all ASCII keys are handled as text below + // but this makes it impossible to detect CTRL-ASCII... more to follow + BYTE ch = 0; + + switch (key.keysym.sym) + { + case SDLK_RETURN: + { + ch = 0x0d; + break; + } + case SDLK_BACKSPACE: // same as AppleWin + case SDLK_LEFT: + { + ch = 0x08; + break; + } + case SDLK_RIGHT: + { + ch = 0x15; + break; + } + case SDLK_UP: + { + ch = 0x0b; + break; + } + case SDLK_DOWN: + { + ch = 0x0a; + break; + } + case SDLK_DELETE: + { + ch = 0x7f; + break; + } + case SDLK_ESCAPE: + { + ch = 0x1b; + break; + } + case SDLK_TAB: + { + ch = 0x09; + break; + } + case SDLK_a ... SDLK_z: + { + ch = (key.keysym.sym - SDLK_a) + 0x01; + if (key.keysym.mod & KMOD_CTRL) + { + // ok + } + else if (key.keysym.mod & KMOD_SHIFT) + { + ch += 0x60; + } + else + { + ch += 0x40; + } + break; + } + } + + if (ch) + { + addKeyToBuffer(ch); + std::cerr << "SDL KeyboardEvent: " << std::hex << (int)ch << std::dec << std::endl; + } + } + +} + + +Emulator::Emulator( + const std::shared_ptr & window, + const std::shared_ptr & renderer, + const std::shared_ptr & texture, + const bool fixedSpeed +) + : myWindow(window) + , myRenderer(renderer) + , myTexture(texture) + , myMultiplier(1) + , myFullscreen(false) + , mySpeed(fixedSpeed) +{ + myRect.x = GetFrameBufferBorderWidth(); + myRect.y = GetFrameBufferBorderHeight(); + myRect.w = GetFrameBufferBorderlessWidth(); + myRect.h = GetFrameBufferBorderlessHeight(); + myPitch = GetFrameBufferWidth() * sizeof(bgra_t); +} + +void Emulator::execute(const size_t next) +{ + if (g_nAppMode == MODE_RUNNING) + { + const size_t cyclesToExecute = mySpeed.getCyclesTillNext(next); + + const bool bVideoUpdate = true; + const UINT dwClksPerFrame = NTSC_GetCyclesPerFrame(); + + const DWORD executedCycles = CpuExecute(cyclesToExecute, bVideoUpdate); + + g_dwCyclesThisFrame = (g_dwCyclesThisFrame + executedCycles) % dwClksPerFrame; + GetCardMgr().GetDisk2CardMgr().UpdateDriveState(executedCycles); + MB_PeriodicUpdate(executedCycles); + SpkrUpdate(executedCycles); + } +} + +void Emulator::updateTexture() +{ + SDL_UpdateTexture(myTexture.get(), nullptr, g_pFramebufferbits, myPitch); +} + +void Emulator::refreshVideo() +{ + SDL_RenderCopyEx(myRenderer.get(), myTexture.get(), &myRect, nullptr, 0.0, nullptr, SDL_FLIP_VERTICAL); + SDL_RenderPresent(myRenderer.get()); +} + +void Emulator::processEvents(bool & quit) +{ + SDL_Event e; + while (SDL_PollEvent(&e) != 0) + { + switch (e.type) + { + case SDL_QUIT: + { + quit = true; + break; + } + case SDL_KEYDOWN: + { + processKeyDown(e.key, quit); + break; + } + case SDL_KEYUP: + { + processKeyUp(e.key); + break; + } + case SDL_TEXTINPUT: + { + processText(e.text); + break; + } + } + } +} + +void Emulator::processKeyDown(const SDL_KeyboardEvent & key, bool & quit) +{ + // scancode vs keycode + // scancode is relative to the position on the keyboard + // keycode is what the os maps it to + // if the user has decided to change the layout, we just go with it and use the keycode + if (!key.repeat) + { + switch (key.keysym.sym) + { + case SDLK_F12: + { + Snapshot_LoadState(); + mySpeed.reset(); + break; + } + case SDLK_F11: + { + const std::string & pathname = Snapshot_GetPathname(); + const std::string message = "Do you want to save the state to " + pathname + "?"; + SoundCore_SetFade(FADE_OUT); + if (show_yes_no_dialog(myWindow, "Save state", message)) + { + Snapshot_SaveState(); + } + SoundCore_SetFade(FADE_IN); + mySpeed.reset(); + break; + } + case SDLK_F9: + { + cycleVideoType(myWindow); + break; + } + case SDLK_F6: + { + if ((key.keysym.mod & KMOD_CTRL) && (key.keysym.mod & KMOD_SHIFT)) + { + cycle50ScanLines(myWindow); + } + else if (key.keysym.mod & KMOD_CTRL) + { + myMultiplier = myMultiplier == 1 ? 2 : 1; + const int sw = GetFrameBufferBorderlessWidth(); + const int sh = GetFrameBufferBorderlessHeight(); + SDL_SetWindowSize(myWindow.get(), sw * myMultiplier, sh * myMultiplier); + } + else if (!(key.keysym.mod & KMOD_SHIFT)) + { + myFullscreen = !myFullscreen; + SDL_SetWindowFullscreen(myWindow.get(), myFullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + } + break; + } + case SDLK_F5: + { + CardManager & cardManager = GetCardMgr(); + if (cardManager.QuerySlot(SLOT6) == CT_Disk2) + { + dynamic_cast(cardManager.GetObj(SLOT6))->DriveSwap(); + } + break; + } + case SDLK_F2: + { + if (key.keysym.mod & KMOD_CTRL) + { + CtrlReset(); + } + else + { + ResetMachineState(); + } + break; + } + case SDLK_F1: + { + SDirectSound::printInfo(); + break; + } + case SDLK_LALT: + { + Paddle::setButtonPressed(Paddle::ourOpenApple); + break; + } + case SDLK_RALT: + { + Paddle::setButtonPressed(Paddle::ourSolidApple); + break; + } + case SDLK_PAUSE: + { + switch (g_nAppMode) + { + case MODE_RUNNING: + g_nAppMode = MODE_PAUSED; + SoundCore_SetFade(FADE_OUT); + break; + case MODE_PAUSED: + g_nAppMode = MODE_RUNNING; + SoundCore_SetFade(FADE_IN); + mySpeed.reset(); + break; + } + updateWindowTitle(myWindow); + break; + } + } + } + + processAppleKey(key); + +#ifdef KEY_LOGGING_VERBOSE + std::cerr << "KEY DOWN: " << key.keysym.scancode << "," << key.keysym.sym << "," << key.keysym.mod << "," << bool(key.repeat) << std::endl; +#endif + +} + +void Emulator::processKeyUp(const SDL_KeyboardEvent & key) +{ + switch (key.keysym.sym) + { + case SDLK_LALT: + { + Paddle::setButtonReleased(Paddle::ourOpenApple); + break; + } + case SDLK_RALT: + { + Paddle::setButtonReleased(Paddle::ourSolidApple); + break; + } + } + +#ifdef KEY_LOGGING_VERBOSE + std::cerr << "KEY UP: " << key.keysym.scancode << "," << key.keysym.sym << "," << key.keysym.mod << "," << bool(key.repeat) << std::endl; +#endif + +} + +void Emulator::processText(const SDL_TextInputEvent & text) +{ + if (strlen(text.text) == 1) + { + const char key = text.text[0]; + switch (key) { + case 0x20 ... 0x40: + case 0x5b ... 0x60: + case 0x7b ... 0x7e: + { + // not the letters + // this is very simple, but one cannot handle CRTL-key combination. + addKeyToBuffer(key); + std::cerr << "SDL TextInputEvent: " << std::hex << (int)key << std::dec << std::endl; + break; + } + } + } +} diff --git a/source/frontends/retro/emulator.h b/source/frontends/retro/emulator.h new file mode 100644 index 00000000..265ee440 --- /dev/null +++ b/source/frontends/retro/emulator.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +#include "frontends/common2/speed.h" + +class Emulator +{ +public: + Emulator( + const std::shared_ptr & window, + const std::shared_ptr & renderer, + const std::shared_ptr & texture, + const bool fixedSpeed + ); + + void execute(const size_t milliseconds); + + void updateTexture(); + void refreshVideo(); + + void processEvents(bool & quit); + +private: + void processKeyDown(const SDL_KeyboardEvent & key, bool & quit); + void processKeyUp(const SDL_KeyboardEvent & key); + void processText(const SDL_TextInputEvent & text); + + const std::shared_ptr myWindow; + const std::shared_ptr myRenderer; + const std::shared_ptr myTexture; + + int myMultiplier; + bool myFullscreen; + + Speed mySpeed; + + SDL_Rect myRect; + int myPitch; +}; diff --git a/source/frontends/retro/environment.cpp b/source/frontends/retro/environment.cpp new file mode 100644 index 00000000..c63678df --- /dev/null +++ b/source/frontends/retro/environment.cpp @@ -0,0 +1,17 @@ +#include "frontends/retro/environment.h" + +#include +#include + + +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; diff --git a/source/frontends/retro/environment.h b/source/frontends/retro/environment.h new file mode 100644 index 00000000..079568c6 --- /dev/null +++ b/source/frontends/retro/environment.h @@ -0,0 +1,5 @@ +#include "libretro.h" + +void fallback_log(enum retro_log_level level, const char *fmt, ...); +extern retro_log_callback logging; +extern retro_log_printf_t log_cb; diff --git a/source/frontends/retro/libretro.cpp b/source/frontends/retro/libretro.cpp new file mode 100644 index 00000000..b693a5e4 --- /dev/null +++ b/source/frontends/retro/libretro.cpp @@ -0,0 +1,365 @@ +#include "libretro.h" +#include + +#include "StdAfx.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 +#include + +#include "frontends/common2/programoptions.h" +#include "frontends/common2/configuration.h" +#include "frontends/common2/speed.h" +#include "frontends/retro/environment.h" + +namespace +{ + bool use_audio_cb; + float last_aspect; + float last_sample_rate; + std::string retro_base_directory; + + retro_video_refresh_t video_cb; + retro_audio_sample_t audio_cb; + retro_audio_sample_batch_t audio_batch_cb; + retro_input_poll_t input_poll_cb; + retro_input_state_t input_state_cb; + retro_rumble_interface rumble; + + retro_environment_t environ_cb; + + Speed speed(true); + + 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"); + + ImageInitialize(); + 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(); + ImageDestroy(); + LogDone(); + RiffFinishWriteFile(); + } + +} + +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) +{ +} + +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; + } + + EmulatorOptions options; + options.memclear = g_nMemoryClearType; + options.log = true; + options.useQtIni = true; + + if (options.log) + { + LogInit(); + } + + InitializeRegistry(options); + initialiseEmulator(); +} + +void retro_deinit(void) +{ + log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__); + uninitialiseEmulator(); +} + +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); +} + +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 = "dsk"; +} + + +void retro_get_system_av_info(retro_system_av_info *info) +{ + log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__); + float aspect = 0.0f; + float sampling_rate = 30000.0f; + + info->geometry.base_width = GetFrameBufferWidth(); + info->geometry.base_height = GetFrameBufferHeight(); + info->geometry.max_width = GetFrameBufferWidth(); + info->geometry.max_height = GetFrameBufferHeight(); + info->geometry.aspect_ratio = aspect; + + last_aspect = aspect; + last_sample_rate = sampling_rate; +} + +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[] = + { + { "Nintendo DS", RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0) }, + }; + + static const retro_controller_info ports[] = + { + { controllers, 1 }, + { NULL, 0 }, + }; + + cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports); +} + +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) +{ + 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); + + g_dwCyclesThisFrame = (g_dwCyclesThisFrame + executedCycles) % dwClksPerFrame; + GetCardMgr().GetDisk2CardMgr().UpdateDriveState(executedCycles); + MB_PeriodicUpdate(executedCycles); + SpkrUpdate(executedCycles); + } + + // log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__); + const size_t pitch = GetFrameBufferWidth() * 4; + input_poll_cb(); + video_cb(g_pFramebufferbits, GetFrameBufferWidth(), GetFrameBufferHeight(), pitch); +} + +bool retro_load_game(const retro_game_info *info) +{ + log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__); + retro_input_descriptor desc[] = + { + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" }, + { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" }, + { 0 }, + }; + + environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc); + + 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; + } + + const bool ok = DoDiskInsert(SLOT6, DRIVE_1, info->path); + log_cb(RETRO_LOG_INFO, "Game path: %s:%d\n", info->path, ok); + + return true; +} + +void retro_unload_game(void) +{ + 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; +} diff --git a/source/frontends/retro/rdirectsound.cpp b/source/frontends/retro/rdirectsound.cpp new file mode 100644 index 00000000..2d5b6493 --- /dev/null +++ b/source/frontends/retro/rdirectsound.cpp @@ -0,0 +1,10 @@ +#include + +// Mockingboard +void registerSoundBuffer(IDirectSoundBuffer * buffer) +{ +} + +void unregisterSoundBuffer(IDirectSoundBuffer * buffer) +{ +} diff --git a/source/frontends/sa2/CMakeLists.txt b/source/frontends/sa2/CMakeLists.txt index aab73692..eab44af5 100644 --- a/source/frontends/sa2/CMakeLists.txt +++ b/source/frontends/sa2/CMakeLists.txt @@ -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 From 40b838aeb9941665d6bec999edcfd4eafe28f418 Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Fri, 11 Dec 2020 19:38:34 +0000 Subject: [PATCH 02/15] Temporary step to load resources in libretro. Signed-off-by: Andrea Odetti --- source/frontends/common2/config.h.in | 3 +++ source/frontends/common2/resources.cpp | 27 +++++++++++++++----------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/source/frontends/common2/config.h.in b/source/frontends/common2/config.h.in index d074ea8d..ffe5c851 100644 --- a/source/frontends/common2/config.h.in +++ b/source/frontends/common2/config.h.in @@ -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@" diff --git a/source/frontends/common2/resources.cpp b/source/frontends/common2/resources.cpp index 012d6870..325a26e8 100644 --- a/source/frontends/common2/resources.cpp +++ b/source/frontends/common2/resources.cpp @@ -27,6 +27,8 @@ namespace std::string getResourcePathImpl() { + std::vector 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 "/home/andrea/projects/cvs/A2E/resource/"; + + throw std::runtime_error("Cannot found the resource path"); } } From 7f441060f21a02b973fceb6739e42734cc4b664f Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Fri, 11 Dec 2020 19:38:47 +0000 Subject: [PATCH 03/15] Some change to better structure the code. Signed-off-by: Andrea Odetti --- source/frontends/retro/libretro.cpp | 67 ++++++++++++++++------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/source/frontends/retro/libretro.cpp b/source/frontends/retro/libretro.cpp index b693a5e4..287626d1 100644 --- a/source/frontends/retro/libretro.cpp +++ b/source/frontends/retro/libretro.cpp @@ -35,9 +35,6 @@ namespace { - bool use_audio_cb; - float last_aspect; - float last_sample_rate; std::string retro_base_directory; retro_video_refresh_t video_cb; @@ -45,11 +42,10 @@ namespace retro_audio_sample_batch_t audio_batch_cb; retro_input_poll_t input_poll_cb; retro_input_state_t input_state_cb; - retro_rumble_interface rumble; retro_environment_t environ_cb; - Speed speed(true); + Speed speed(true); // fixed speed void initialiseEmulator() { @@ -63,7 +59,6 @@ namespace g_nAppMode = MODE_RUNNING; LogFileOutput("Initialisation\n"); - ImageInitialize(); g_bFullSpeed = false; LoadConfiguration(); @@ -103,11 +98,39 @@ namespace CpuDestroy(); GetCardMgr().GetDisk2CardMgr().Destroy(); - ImageDestroy(); LogDone(); RiffFinishWriteFile(); } + void runOneFrame() + { + 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); + + g_dwCyclesThisFrame = (g_dwCyclesThisFrame + executedCycles) % dwClksPerFrame; + GetCardMgr().GetDisk2CardMgr().UpdateDriveState(executedCycles); + MB_PeriodicUpdate(executedCycles); + SpkrUpdate(executedCycles); + } + } + + void processInputEvents() + { + input_poll_cb(); + + const int16_t is_down = input_state_cb(0, RETRO_DEVICE_KEYBOARD, 0, RETROK_a); + if (is_down) + { + log_cb(RETRO_LOG_INFO, "RA2: %s. A is down\n", __FUNCTION__); + } + } + } int MessageBox(HWND, const char * text, const char * caption, UINT type) @@ -185,17 +208,15 @@ void retro_get_system_info(retro_system_info *info) void retro_get_system_av_info(retro_system_av_info *info) { log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__); - float aspect = 0.0f; - float sampling_rate = 30000.0f; info->geometry.base_width = GetFrameBufferWidth(); info->geometry.base_height = GetFrameBufferHeight(); info->geometry.max_width = GetFrameBufferWidth(); info->geometry.max_height = GetFrameBufferHeight(); - info->geometry.aspect_ratio = aspect; + info->geometry.aspect_ratio = 0; - last_aspect = aspect; - last_sample_rate = sampling_rate; + info->timing.fps = 60; + info->timing.sample_rate = 0; } void retro_set_environment(retro_environment_t cb) @@ -210,7 +231,7 @@ void retro_set_environment(retro_environment_t cb) static const retro_controller_description controllers[] = { - { "Nintendo DS", RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0) }, + { "Apple Keyboard", RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_KEYBOARD, 0) }, }; static const retro_controller_info ports[] = @@ -254,24 +275,10 @@ void retro_set_video_refresh(retro_video_refresh_t cb) void retro_run(void) { - 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); - - g_dwCyclesThisFrame = (g_dwCyclesThisFrame + executedCycles) % dwClksPerFrame; - GetCardMgr().GetDisk2CardMgr().UpdateDriveState(executedCycles); - MB_PeriodicUpdate(executedCycles); - SpkrUpdate(executedCycles); - } - - // log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__); - const size_t pitch = GetFrameBufferWidth() * 4; input_poll_cb(); + processInputEvents(); + runOneFrame(); + const size_t pitch = GetFrameBufferWidth() * 4; video_cb(g_pFramebufferbits, GetFrameBufferWidth(), GetFrameBufferHeight(), pitch); } From b95bcadfe938229f54faf62658c1534c13f0f379 Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Fri, 11 Dec 2020 20:52:40 +0000 Subject: [PATCH 04/15] Fix bitmap loading. Signed-off-by: Andrea Odetti --- source/frontends/retro/bitmaps.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/source/frontends/retro/bitmaps.cpp b/source/frontends/retro/bitmaps.cpp index 0de23574..5499cbc4 100644 --- a/source/frontends/retro/bitmaps.cpp +++ b/source/frontends/retro/bitmaps.cpp @@ -5,6 +5,7 @@ #include #include +#include namespace { @@ -94,8 +95,6 @@ HBITMAP LoadBitmap(HINSTANCE hInstance, const char * resource) const std::string filename = getFilename(resource); const std::string path = getResourcePath() + filename; - log_cb(RETRO_LOG_INFO, "RA2: %s. Path = %s\n", __FUNCTION__, path.c_str()); - std::vector buffer; readFileToBuffer(path, buffer); @@ -107,10 +106,21 @@ HBITMAP LoadBitmap(HINSTANCE hInstance, const char * resource) uint32_t size; const bool res = getBitmapData(buffer, width, height, bpp, data, size); - if (res) + 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.assign(data, data + size); + 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; } } @@ -135,9 +145,7 @@ LONG GetBitmapBits(HBITMAP hbit, LONG cb, LPVOID lpvBits) const size_t copied = std::min(requested, size); char * dest = static_cast(lpvBits); - for (size_t i = 0; i < copied; ++i) - { - dest[i] = ~bits[i]; - } + memcpy(dest, bits, copied); + return copied; } From cec0c10286d23d0f41f65ea1d0e779349a365864 Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Sat, 12 Dec 2020 12:22:17 +0000 Subject: [PATCH 05/15] libretro: first "working" versions. draws video upside down accepts keys (O crashes, and others have overloaded meaning) Signed-off-by: Andrea Odetti --- source/frontends/retro/CMakeLists.txt | 3 + source/frontends/retro/environment.cpp | 9 ++ source/frontends/retro/environment.h | 10 ++ source/frontends/retro/game.cpp | 207 +++++++++++++++++++++++++ source/frontends/retro/game.h | 27 ++++ source/frontends/retro/interface.cpp | 22 +++ source/frontends/retro/libretro.cpp | 200 ++++-------------------- source/frontends/retro/run | 1 + 8 files changed, 306 insertions(+), 173 deletions(-) create mode 100644 source/frontends/retro/game.cpp create mode 100644 source/frontends/retro/game.h create mode 100644 source/frontends/retro/interface.cpp create mode 100644 source/frontends/retro/run diff --git a/source/frontends/retro/CMakeLists.txt b/source/frontends/retro/CMakeLists.txt index 963c7ce6..ce4af256 100644 --- a/source/frontends/retro/CMakeLists.txt +++ b/source/frontends/retro/CMakeLists.txt @@ -4,6 +4,9 @@ add_library(ra2 SHARED libretro.cpp bitmaps.cpp rdirectsound.cpp + interface.cpp + events.cpp + game.cpp ) target_include_directories(ra2 PRIVATE diff --git a/source/frontends/retro/environment.cpp b/source/frontends/retro/environment.cpp index c63678df..16466c5a 100644 --- a/source/frontends/retro/environment.cpp +++ b/source/frontends/retro/environment.cpp @@ -15,3 +15,12 @@ void fallback_log(enum retro_log_level level, const char *fmt, ...) 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; diff --git a/source/frontends/retro/environment.h b/source/frontends/retro/environment.h index 079568c6..4810a98b 100644 --- a/source/frontends/retro/environment.h +++ b/source/frontends/retro/environment.h @@ -1,5 +1,15 @@ #include "libretro.h" +#include + 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; diff --git a/source/frontends/retro/game.cpp b/source/frontends/retro/game.cpp new file mode 100644 index 00000000..bf91d860 --- /dev/null +++ b/source/frontends/retro/game.cpp @@ -0,0 +1,207 @@ +#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 "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); + } + } + + std::unordered_map getKeymap() + { + std::unordered_map km; + for (unsigned key = RETROK_a; key <= RETROK_z; ++key) + { + km[key] = 0x41 + (key - RETROK_a); + } + for (unsigned key = RETROK_0; key <= RETROK_9; ++key) + { + km[key] = 0x30 + (key - RETROK_0); + } + km[RETROK_SPACE] = 0x20; + km[RETROK_RETURN] = 0x0d; + km[RETROK_UP] = 0x0b; + km[RETROK_DOWN] = 0x0a; + km[RETROK_LEFT] = 0x08; + km[RETROK_UP] = 0x15; + km[RETROK_DELETE] = 0x7f; + km[RETROK_BACKSPACE] = 0x08; + km[RETROK_ESCAPE] = 0x1b; + + return km; + } + +} + +Game::Game() : myKeymap(getKeymap()), mySpeed(true) +{ + EmulatorOptions options; + options.memclear = g_nMemoryClearType; + options.log = true; + options.useQtIni = true; + + if (options.log) + { + LogInit(); + } + + InitializeRegistry(options); + initialiseEmulator(); +} + +Game::~Game() +{ + uninitialiseEmulator(); +} + +void Game::executeOneFrame() +{ + runOneFrame(mySpeed); +} + +void Game::processInputEvents() +{ + input_poll_cb(); + processKeyboardEvents(); +} + +void Game::processKeyboardEvents() +{ + std::vector newKeys; + + for (auto it : myKeymap) + { + const unsigned key = it.first; + const int16_t isDown = input_state_cb(0, RETRO_DEVICE_KEYBOARD, 0, key); + + // pressed now, not pressed before + if (isDown && !myKeystate[key]) + { + // add + newKeys.push_back(it.second); + } + + myKeystate[key] = isDown != 0; + } + + // pass to AppleWin. in which order? + for (unsigned key : newKeys) + { + addKeyToBuffer(key); + } + +} + +void Game::drawVideoBuffer() +{ + const size_t pitch = GetFrameBufferWidth() * 4; + video_cb(g_pFramebufferbits, GetFrameBufferWidth(), GetFrameBufferHeight(), pitch); +} + +bool Game::loadGame(const char * path) +{ + const bool ok = DoDiskInsert(SLOT6, DRIVE_1, path); + return ok; +} diff --git a/source/frontends/retro/game.h b/source/frontends/retro/game.h new file mode 100644 index 00000000..07d27db9 --- /dev/null +++ b/source/frontends/retro/game.h @@ -0,0 +1,27 @@ +#pragma once + +#include "frontends/common2/speed.h" + +#include + +class Game +{ +public: + Game(); + ~Game(); + + bool loadGame(const char * path); + + void executeOneFrame(); + void processInputEvents(); + + void drawVideoBuffer(); + + private: + const std::unordered_map myKeymap; + + Speed mySpeed; // fixed speed + std::unordered_map myKeystate; + + void processKeyboardEvents(); +}; diff --git a/source/frontends/retro/interface.cpp b/source/frontends/retro/interface.cpp new file mode 100644 index 00000000..923aafae --- /dev/null +++ b/source/frontends/retro/interface.cpp @@ -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) +{ +} diff --git a/source/frontends/retro/libretro.cpp b/source/frontends/retro/libretro.cpp index 287626d1..4819ee22 100644 --- a/source/frontends/retro/libretro.cpp +++ b/source/frontends/retro/libretro.cpp @@ -1,154 +1,18 @@ #include "libretro.h" -#include +#include +#include #include "StdAfx.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/version.h" -#include -#include - -#include "frontends/common2/programoptions.h" -#include "frontends/common2/configuration.h" -#include "frontends/common2/speed.h" +#include "frontends/retro/game.h" #include "frontends/retro/environment.h" namespace { - std::string retro_base_directory; - - retro_video_refresh_t video_cb; - retro_audio_sample_t audio_cb; - retro_audio_sample_batch_t audio_batch_cb; - retro_input_poll_t input_poll_cb; - retro_input_state_t input_state_cb; - - retro_environment_t environ_cb; - - Speed speed(true); // fixed speed - - 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() - { - 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); - - g_dwCyclesThisFrame = (g_dwCyclesThisFrame + executedCycles) % dwClksPerFrame; - GetCardMgr().GetDisk2CardMgr().UpdateDriveState(executedCycles); - MB_PeriodicUpdate(executedCycles); - SpkrUpdate(executedCycles); - } - } - - void processInputEvents() - { - input_poll_cb(); - - const int16_t is_down = input_state_cb(0, RETRO_DEVICE_KEYBOARD, 0, RETROK_a); - if (is_down) - { - log_cb(RETRO_LOG_INFO, "RA2: %s. A is down\n", __FUNCTION__); - } - } - -} - -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) -{ + std::unique_ptr game; } void retro_init(void) @@ -159,25 +23,11 @@ void retro_init(void) { retro_base_directory = dir; } - - EmulatorOptions options; - options.memclear = g_nMemoryClearType; - options.log = true; - options.useQtIni = true; - - if (options.log) - { - LogInit(); - } - - InitializeRegistry(options); - initialiseEmulator(); } void retro_deinit(void) { log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__); - uninitialiseEmulator(); } unsigned retro_api_version(void) @@ -275,26 +125,14 @@ void retro_set_video_refresh(retro_video_refresh_t cb) void retro_run(void) { - input_poll_cb(); - processInputEvents(); - runOneFrame(); - const size_t pitch = GetFrameBufferWidth() * 4; - video_cb(g_pFramebufferbits, GetFrameBufferWidth(), GetFrameBufferHeight(), pitch); + game->processInputEvents(); + game->executeOneFrame(); + game->drawVideoBuffer(); } bool retro_load_game(const retro_game_info *info) { log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__); - retro_input_descriptor desc[] = - { - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" }, - { 0 }, - }; - - environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc); enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888; if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) @@ -303,14 +141,30 @@ bool retro_load_game(const retro_game_info *info) return false; } - const bool ok = DoDiskInsert(SLOT6, DRIVE_1, info->path); - log_cb(RETRO_LOG_INFO, "Game path: %s:%d\n", info->path, ok); + try + { + game.reset(new Game); + const bool ok = game->loadGame(info->path); - return true; + 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__); } diff --git a/source/frontends/retro/run b/source/frontends/retro/run new file mode 100644 index 00000000..423882cf --- /dev/null +++ b/source/frontends/retro/run @@ -0,0 +1 @@ +retroarch -L source/frontends/retro/libra2.so ../Disks/NoSlotClockTest.dsk From 120cb8be50896783c0030495057fa256a8d7e246 Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Sat, 12 Dec 2020 13:24:06 +0000 Subject: [PATCH 06/15] It is not clear to me how the Input API works. This is copied (without understanding) from other examples. Signed-off-by: Andrea Odetti --- source/frontends/retro/environment.cpp | 2 ++ source/frontends/retro/environment.h | 4 ++++ source/frontends/retro/libretro.cpp | 7 +++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/source/frontends/retro/environment.cpp b/source/frontends/retro/environment.cpp index 16466c5a..1355b256 100644 --- a/source/frontends/retro/environment.cpp +++ b/source/frontends/retro/environment.cpp @@ -24,3 +24,5 @@ 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] = {}; diff --git a/source/frontends/retro/environment.h b/source/frontends/retro/environment.h index 4810a98b..da36b741 100644 --- a/source/frontends/retro/environment.h +++ b/source/frontends/retro/environment.h @@ -13,3 +13,7 @@ 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]; diff --git a/source/frontends/retro/libretro.cpp b/source/frontends/retro/libretro.cpp index 4819ee22..81a7352f 100644 --- a/source/frontends/retro/libretro.cpp +++ b/source/frontends/retro/libretro.cpp @@ -40,6 +40,9 @@ 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) @@ -78,7 +81,7 @@ void retro_set_environment(retro_environment_t cb) log_cb = logging.log; else log_cb = fallback_log; - + /* static const retro_controller_description controllers[] = { { "Apple Keyboard", RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_KEYBOARD, 0) }, @@ -90,7 +93,7 @@ void retro_set_environment(retro_environment_t cb) { NULL, 0 }, }; - cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports); + cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);*/ } void retro_set_audio_sample(retro_audio_sample_t cb) From 54232b988bb9ce04ccdf8fbb509956f941939e84 Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Sat, 12 Dec 2020 13:26:07 +0000 Subject: [PATCH 07/15] Accept a few more disk types. Signed-off-by: Andrea Odetti --- source/frontends/retro/libretro.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/frontends/retro/libretro.cpp b/source/frontends/retro/libretro.cpp index 81a7352f..4649010c 100644 --- a/source/frontends/retro/libretro.cpp +++ b/source/frontends/retro/libretro.cpp @@ -54,7 +54,7 @@ void retro_get_system_info(retro_system_info *info) info->library_name = "AppleWin"; info->library_version = version.c_str(); info->need_fullpath = true; - info->valid_extensions = "dsk"; + info->valid_extensions = "bin|do|dsk|nib|po|gz|woz|zip|2mg|2img|iie|apl|hdv"; } From 94b7d6609670bf9ed8afd161d39196ea268c0ae1 Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Sat, 12 Dec 2020 13:45:37 +0000 Subject: [PATCH 08/15] Remove hard-coded libretro path. Signed-off-by: Andrea Odetti --- source/frontends/retro/CMakeLists.txt | 44 ++++++++++++++++----------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/source/frontends/retro/CMakeLists.txt b/source/frontends/retro/CMakeLists.txt index ce4af256..7e324901 100644 --- a/source/frontends/retro/CMakeLists.txt +++ b/source/frontends/retro/CMakeLists.txt @@ -1,20 +1,28 @@ -add_library(ra2 SHARED - ~/projects/games/retroarch/libretro-super/retroarch/libretro-common/formats/bmp/rbmp.c - environment.cpp - libretro.cpp - bitmaps.cpp - rdirectsound.cpp - interface.cpp - events.cpp - game.cpp - ) +# there is no simple package for libretro-common +# this requires user input +set(LIBRETRO_PATH NONE CACHE PATH "path to libretro-common") -target_include_directories(ra2 PRIVATE - ~/projects/games/retroarch/libretro-super/retroarch/libretro-common - ~/projects/games/retroarch/libretro-super/retroarch/libretro-common/include - ) +if (IS_DIRECTORY ${LIBRETRO_PATH}) + message("Using LIBRETRO_PATH=${LIBRETRO_PATH}") -target_link_libraries(ra2 PRIVATE - appleii - common2 - ) + add_library(ra2 SHARED + environment.cpp + libretro.cpp + bitmaps.cpp + rdirectsound.cpp + interface.cpp + events.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() From 66b62ecca1fe3265d326d5e0754ea7bcfaa3bdf5 Mon Sep 17 00:00:00 2001 From: Andrea Date: Sat, 12 Dec 2020 13:48:33 +0000 Subject: [PATCH 09/15] Update linux.md --- linux.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/linux.md b/linux.md index ea7a2bc4..7fc031e5 100644 --- a/linux.md +++ b/linux.md @@ -6,6 +6,7 @@ * [applen](#applen) * [qapple](#qapple) * [sa2](#sa2) + * [libretro](#ra2) * [Build](#build) * [Checkout](#checkout) * [Fedora](#fedora) @@ -88,6 +89,16 @@ 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, with minimal keyboard support and upside down video. + +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. From ab655cda40cc3823a64601258233914e747a9808 Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Sat, 12 Dec 2020 18:42:44 +0000 Subject: [PATCH 10/15] Move keyboard to a callback system. The "switch" is the same as the SDL version. Signed-off-by: Andrea Odetti --- source/frontends/retro/game.cpp | 127 ++++++++++++++++++---------- source/frontends/retro/game.h | 9 +- source/frontends/retro/libretro.cpp | 2 + 3 files changed, 86 insertions(+), 52 deletions(-) diff --git a/source/frontends/retro/game.cpp b/source/frontends/retro/game.cpp index bf91d860..3a7bc0ea 100644 --- a/source/frontends/retro/game.cpp +++ b/source/frontends/retro/game.cpp @@ -109,38 +109,13 @@ namespace } } - std::unordered_map getKeymap() - { - std::unordered_map km; - for (unsigned key = RETROK_a; key <= RETROK_z; ++key) - { - km[key] = 0x41 + (key - RETROK_a); - } - for (unsigned key = RETROK_0; key <= RETROK_9; ++key) - { - km[key] = 0x30 + (key - RETROK_0); - } - km[RETROK_SPACE] = 0x20; - km[RETROK_RETURN] = 0x0d; - km[RETROK_UP] = 0x0b; - km[RETROK_DOWN] = 0x0a; - km[RETROK_LEFT] = 0x08; - km[RETROK_UP] = 0x15; - km[RETROK_DELETE] = 0x7f; - km[RETROK_BACKSPACE] = 0x08; - km[RETROK_ESCAPE] = 0x1b; - - return km; - } - } -Game::Game() : myKeymap(getKeymap()), mySpeed(true) +Game::Game() : mySpeed(true) { EmulatorOptions options; options.memclear = g_nMemoryClearType; options.log = true; - options.useQtIni = true; if (options.log) { @@ -164,34 +139,96 @@ void Game::executeOneFrame() void Game::processInputEvents() { input_poll_cb(); - processKeyboardEvents(); } -void Game::processKeyboardEvents() +void Game::keyboardCallback(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers) { - std::vector newKeys; - - for (auto it : myKeymap) + if (down) { - const unsigned key = it.first; - const int16_t isDown = input_state_cb(0, RETRO_DEVICE_KEYBOARD, 0, key); - - // pressed now, not pressed before - if (isDown && !myKeystate[key]) + BYTE ch = 0; + switch (keycode) { - // add - newKeys.push_back(it.second); + 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_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; + } } - myKeystate[key] = isDown != 0; - } + 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; + } + } + } - // pass to AppleWin. in which order? - for (unsigned key : newKeys) - { - addKeyToBuffer(key); + if (ch) + { + addKeyToBuffer(ch); + log_cb(RETRO_LOG_INFO, "RA2: %s - %02x\n", __FUNCTION__, ch); + } } - } void Game::drawVideoBuffer() diff --git a/source/frontends/retro/game.h b/source/frontends/retro/game.h index 07d27db9..d4ccbca3 100644 --- a/source/frontends/retro/game.h +++ b/source/frontends/retro/game.h @@ -2,8 +2,6 @@ #include "frontends/common2/speed.h" -#include - class Game { public: @@ -17,11 +15,8 @@ public: void drawVideoBuffer(); + static void keyboardCallback(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers); + private: - const std::unordered_map myKeymap; - Speed mySpeed; // fixed speed - std::unordered_map myKeystate; - - void processKeyboardEvents(); }; diff --git a/source/frontends/retro/libretro.cpp b/source/frontends/retro/libretro.cpp index 4649010c..7d80e46f 100644 --- a/source/frontends/retro/libretro.cpp +++ b/source/frontends/retro/libretro.cpp @@ -94,6 +94,8 @@ void retro_set_environment(retro_environment_t cb) }; 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) From e461847a4985712480f2a685bbc0bd91d57e845f Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Sat, 12 Dec 2020 19:25:43 +0000 Subject: [PATCH 11/15] libretro: add support for Open / Solid Apple keys. Signed-off-by: Andrea Odetti --- source/frontends/retro/game.cpp | 171 +++++++++++++++++++------------- source/frontends/retro/game.h | 6 +- 2 files changed, 109 insertions(+), 68 deletions(-) diff --git a/source/frontends/retro/game.cpp b/source/frontends/retro/game.cpp index 3a7bc0ea..31b1a584 100644 --- a/source/frontends/retro/game.cpp +++ b/source/frontends/retro/game.cpp @@ -26,6 +26,7 @@ #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" @@ -145,88 +146,124 @@ void Game::keyboardCallback(bool down, unsigned keycode, uint32_t character, uin { if (down) { - BYTE ch = 0; - switch (keycode) + 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: { - 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) { - ch = 0x0d; - break; + // ok } - case RETROK_BACKSPACE: // same as AppleWin - case RETROK_LEFT: + else if (key_modifiers & RETROKMOD_SHIFT) { - ch = 0x08; - break; + ch += 0x60; } - case RETROK_RIGHT: + else { - ch = 0x15; - break; + ch += 0x40; } - case RETROK_UP: + break; + } + } + + if (!ch) + { + switch (character) { + case 0x20 ... 0x40: + case 0x5b ... 0x60: + case 0x7b ... 0x7e: { - 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_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; - } + // not the letters + // this is very simple, but one cannot handle CRTL-key combination. + ch = character; break; } } + } - if (!ch) + 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: { - 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; - } - } + Paddle::setButtonReleased(Paddle::ourOpenApple); + break; } - - if (ch) + case RETROK_RALT: { - addKeyToBuffer(ch); - log_cb(RETRO_LOG_INFO, "RA2: %s - %02x\n", __FUNCTION__, ch); + Paddle::setButtonReleased(Paddle::ourSolidApple); + break; } } } diff --git a/source/frontends/retro/game.h b/source/frontends/retro/game.h index d4ccbca3..9181f977 100644 --- a/source/frontends/retro/game.h +++ b/source/frontends/retro/game.h @@ -17,6 +17,10 @@ public: static void keyboardCallback(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers); - private: +private: Speed mySpeed; // fixed speed + + static void processKeyDown(unsigned keycode, uint32_t character, uint16_t key_modifiers); + static void processKeyUp(unsigned keycode, uint32_t character, uint16_t key_modifiers); + }; From c2f4d8ed3417a8f89cf8a789c3e040a5af567953 Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Sat, 12 Dec 2020 19:27:28 +0000 Subject: [PATCH 12/15] Bite the bullet and flip the video buffer in software. Until a better solution is found. Signed-off-by: Andrea Odetti --- linux.md | 6 +++++- source/frontends/retro/game.cpp | 22 +++++++++++++++++++--- source/frontends/retro/game.h | 4 ++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/linux.md b/linux.md index 7fc031e5..4afe0ea3 100644 --- a/linux.md +++ b/linux.md @@ -91,7 +91,11 @@ See [sa2](source/frontends/sa2/README.md). ### ra2 -There is an initial [libretro](https://docs.libretro.com/development/cores/developing-cores/) core, with minimal keyboard support and upside down video. +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`` diff --git a/source/frontends/retro/game.cpp b/source/frontends/retro/game.cpp index 31b1a584..174cf093 100644 --- a/source/frontends/retro/game.cpp +++ b/source/frontends/retro/game.cpp @@ -112,7 +112,10 @@ namespace } -Game::Game() : mySpeed(true) +Game::Game() + : mySpeed(true) + , myWidth(GetFrameBufferWidth()) + , myHeight(GetFrameBufferHeight()) { EmulatorOptions options; options.memclear = g_nMemoryClearType; @@ -125,6 +128,9 @@ Game::Game() : mySpeed(true) InitializeRegistry(options); initialiseEmulator(); + + const size_t size = myWidth * myHeight * sizeof(bgra_t); + myVideoBuffer.resize(size); } Game::~Game() @@ -270,8 +276,18 @@ void Game::processKeyUp(unsigned keycode, uint32_t character, uint16_t key_modif void Game::drawVideoBuffer() { - const size_t pitch = GetFrameBufferWidth() * 4; - video_cb(g_pFramebufferbits, GetFrameBufferWidth(), GetFrameBufferHeight(), pitch); + 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) diff --git a/source/frontends/retro/game.h b/source/frontends/retro/game.h index 9181f977..e68337da 100644 --- a/source/frontends/retro/game.h +++ b/source/frontends/retro/game.h @@ -20,7 +20,11 @@ public: 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 myVideoBuffer; }; From 254ea8dbf6a0d491044a384b9680fedb91d655ac Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Sat, 12 Dec 2020 19:39:22 +0000 Subject: [PATCH 13/15] Build libretro on travis. Signed-off-by: Andrea Odetti --- source/linux/build.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/linux/build.sh b/source/linux/build.sh index 105ee221..977d4702 100755 --- a/source/linux/build.sh +++ b/source/linux/build.sh @@ -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 From 449e3d2d68bd49056891f2e65f0706ddefe4f6e0 Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Sat, 12 Dec 2020 19:42:03 +0000 Subject: [PATCH 14/15] Fix cmake config. Signed-off-by: Andrea Odetti --- source/frontends/retro/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/source/frontends/retro/CMakeLists.txt b/source/frontends/retro/CMakeLists.txt index 7e324901..c2cf3072 100644 --- a/source/frontends/retro/CMakeLists.txt +++ b/source/frontends/retro/CMakeLists.txt @@ -11,7 +11,6 @@ if (IS_DIRECTORY ${LIBRETRO_PATH}) bitmaps.cpp rdirectsound.cpp interface.cpp - events.cpp game.cpp ) From 758e231565eef2e8ded9abb3e0a2eec4aae7f08d Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Sat, 12 Dec 2020 20:07:17 +0000 Subject: [PATCH 15/15] Remove wrong file. Signed-off-by: Andrea Odetti --- source/frontends/retro/emulator.cpp | 384 ---------------------------- source/frontends/retro/emulator.h | 41 --- 2 files changed, 425 deletions(-) delete mode 100644 source/frontends/retro/emulator.cpp delete mode 100644 source/frontends/retro/emulator.h diff --git a/source/frontends/retro/emulator.cpp b/source/frontends/retro/emulator.cpp deleted file mode 100644 index e518bebf..00000000 --- a/source/frontends/retro/emulator.cpp +++ /dev/null @@ -1,384 +0,0 @@ -#include "frontends/sa2/emulator.h" -#include "frontends/sa2/sdirectsound.h" -#include "frontends/sa2/utils.h" - -#include - -#include "linux/videobuffer.h" -#include "linux/paddle.h" -#include "linux/keyboard.h" - -#include "StdAfx.h" -#include "Common.h" -#include "CardManager.h" -#include "Core.h" -#include "Disk.h" -#include "CPU.h" -#include "Frame.h" -#include "Video.h" -#include "Windows/WinVideo.h" -#include "NTSC.h" -#include "Mockingboard.h" -#include "Speaker.h" -#include "Utilities.h" -#include "SaveState.h" -#include "SoundCore.h" - -// #define KEY_LOGGING_VERBOSE - -namespace -{ - - void updateWindowTitle(const std::shared_ptr & win) - { - GetAppleWindowTitle(); - SDL_SetWindowTitle(win.get(), g_pAppTitle.c_str()); - } - - void cycleVideoType(const std::shared_ptr & win) - { - g_eVideoType++; - if (g_eVideoType >= NUM_VIDEO_MODES) - g_eVideoType = 0; - - Config_Save_Video(); - VideoReinitialize(); - VideoRedrawScreen(); - - updateWindowTitle(win); - } - - void cycle50ScanLines(const std::shared_ptr & win) - { - VideoStyle_e videoStyle = GetVideoStyle(); - videoStyle = VideoStyle_e(videoStyle ^ VS_HALF_SCANLINES); - - SetVideoStyle(videoStyle); - - Config_Save_Video(); - VideoReinitialize(); - VideoRedrawScreen(); - - updateWindowTitle(win); - } - - void processAppleKey(const SDL_KeyboardEvent & key) - { - // using keycode (or scan code) one takes a physical view of the keyboard - // ignoring non US layouts - // SHIFT-3 on a A2 keyboard in # while on my UK keyboard is £? - // so for now all ASCII keys are handled as text below - // but this makes it impossible to detect CTRL-ASCII... more to follow - BYTE ch = 0; - - switch (key.keysym.sym) - { - case SDLK_RETURN: - { - ch = 0x0d; - break; - } - case SDLK_BACKSPACE: // same as AppleWin - case SDLK_LEFT: - { - ch = 0x08; - break; - } - case SDLK_RIGHT: - { - ch = 0x15; - break; - } - case SDLK_UP: - { - ch = 0x0b; - break; - } - case SDLK_DOWN: - { - ch = 0x0a; - break; - } - case SDLK_DELETE: - { - ch = 0x7f; - break; - } - case SDLK_ESCAPE: - { - ch = 0x1b; - break; - } - case SDLK_TAB: - { - ch = 0x09; - break; - } - case SDLK_a ... SDLK_z: - { - ch = (key.keysym.sym - SDLK_a) + 0x01; - if (key.keysym.mod & KMOD_CTRL) - { - // ok - } - else if (key.keysym.mod & KMOD_SHIFT) - { - ch += 0x60; - } - else - { - ch += 0x40; - } - break; - } - } - - if (ch) - { - addKeyToBuffer(ch); - std::cerr << "SDL KeyboardEvent: " << std::hex << (int)ch << std::dec << std::endl; - } - } - -} - - -Emulator::Emulator( - const std::shared_ptr & window, - const std::shared_ptr & renderer, - const std::shared_ptr & texture, - const bool fixedSpeed -) - : myWindow(window) - , myRenderer(renderer) - , myTexture(texture) - , myMultiplier(1) - , myFullscreen(false) - , mySpeed(fixedSpeed) -{ - myRect.x = GetFrameBufferBorderWidth(); - myRect.y = GetFrameBufferBorderHeight(); - myRect.w = GetFrameBufferBorderlessWidth(); - myRect.h = GetFrameBufferBorderlessHeight(); - myPitch = GetFrameBufferWidth() * sizeof(bgra_t); -} - -void Emulator::execute(const size_t next) -{ - if (g_nAppMode == MODE_RUNNING) - { - const size_t cyclesToExecute = mySpeed.getCyclesTillNext(next); - - const bool bVideoUpdate = true; - const UINT dwClksPerFrame = NTSC_GetCyclesPerFrame(); - - const DWORD executedCycles = CpuExecute(cyclesToExecute, bVideoUpdate); - - g_dwCyclesThisFrame = (g_dwCyclesThisFrame + executedCycles) % dwClksPerFrame; - GetCardMgr().GetDisk2CardMgr().UpdateDriveState(executedCycles); - MB_PeriodicUpdate(executedCycles); - SpkrUpdate(executedCycles); - } -} - -void Emulator::updateTexture() -{ - SDL_UpdateTexture(myTexture.get(), nullptr, g_pFramebufferbits, myPitch); -} - -void Emulator::refreshVideo() -{ - SDL_RenderCopyEx(myRenderer.get(), myTexture.get(), &myRect, nullptr, 0.0, nullptr, SDL_FLIP_VERTICAL); - SDL_RenderPresent(myRenderer.get()); -} - -void Emulator::processEvents(bool & quit) -{ - SDL_Event e; - while (SDL_PollEvent(&e) != 0) - { - switch (e.type) - { - case SDL_QUIT: - { - quit = true; - break; - } - case SDL_KEYDOWN: - { - processKeyDown(e.key, quit); - break; - } - case SDL_KEYUP: - { - processKeyUp(e.key); - break; - } - case SDL_TEXTINPUT: - { - processText(e.text); - break; - } - } - } -} - -void Emulator::processKeyDown(const SDL_KeyboardEvent & key, bool & quit) -{ - // scancode vs keycode - // scancode is relative to the position on the keyboard - // keycode is what the os maps it to - // if the user has decided to change the layout, we just go with it and use the keycode - if (!key.repeat) - { - switch (key.keysym.sym) - { - case SDLK_F12: - { - Snapshot_LoadState(); - mySpeed.reset(); - break; - } - case SDLK_F11: - { - const std::string & pathname = Snapshot_GetPathname(); - const std::string message = "Do you want to save the state to " + pathname + "?"; - SoundCore_SetFade(FADE_OUT); - if (show_yes_no_dialog(myWindow, "Save state", message)) - { - Snapshot_SaveState(); - } - SoundCore_SetFade(FADE_IN); - mySpeed.reset(); - break; - } - case SDLK_F9: - { - cycleVideoType(myWindow); - break; - } - case SDLK_F6: - { - if ((key.keysym.mod & KMOD_CTRL) && (key.keysym.mod & KMOD_SHIFT)) - { - cycle50ScanLines(myWindow); - } - else if (key.keysym.mod & KMOD_CTRL) - { - myMultiplier = myMultiplier == 1 ? 2 : 1; - const int sw = GetFrameBufferBorderlessWidth(); - const int sh = GetFrameBufferBorderlessHeight(); - SDL_SetWindowSize(myWindow.get(), sw * myMultiplier, sh * myMultiplier); - } - else if (!(key.keysym.mod & KMOD_SHIFT)) - { - myFullscreen = !myFullscreen; - SDL_SetWindowFullscreen(myWindow.get(), myFullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); - } - break; - } - case SDLK_F5: - { - CardManager & cardManager = GetCardMgr(); - if (cardManager.QuerySlot(SLOT6) == CT_Disk2) - { - dynamic_cast(cardManager.GetObj(SLOT6))->DriveSwap(); - } - break; - } - case SDLK_F2: - { - if (key.keysym.mod & KMOD_CTRL) - { - CtrlReset(); - } - else - { - ResetMachineState(); - } - break; - } - case SDLK_F1: - { - SDirectSound::printInfo(); - break; - } - case SDLK_LALT: - { - Paddle::setButtonPressed(Paddle::ourOpenApple); - break; - } - case SDLK_RALT: - { - Paddle::setButtonPressed(Paddle::ourSolidApple); - break; - } - case SDLK_PAUSE: - { - switch (g_nAppMode) - { - case MODE_RUNNING: - g_nAppMode = MODE_PAUSED; - SoundCore_SetFade(FADE_OUT); - break; - case MODE_PAUSED: - g_nAppMode = MODE_RUNNING; - SoundCore_SetFade(FADE_IN); - mySpeed.reset(); - break; - } - updateWindowTitle(myWindow); - break; - } - } - } - - processAppleKey(key); - -#ifdef KEY_LOGGING_VERBOSE - std::cerr << "KEY DOWN: " << key.keysym.scancode << "," << key.keysym.sym << "," << key.keysym.mod << "," << bool(key.repeat) << std::endl; -#endif - -} - -void Emulator::processKeyUp(const SDL_KeyboardEvent & key) -{ - switch (key.keysym.sym) - { - case SDLK_LALT: - { - Paddle::setButtonReleased(Paddle::ourOpenApple); - break; - } - case SDLK_RALT: - { - Paddle::setButtonReleased(Paddle::ourSolidApple); - break; - } - } - -#ifdef KEY_LOGGING_VERBOSE - std::cerr << "KEY UP: " << key.keysym.scancode << "," << key.keysym.sym << "," << key.keysym.mod << "," << bool(key.repeat) << std::endl; -#endif - -} - -void Emulator::processText(const SDL_TextInputEvent & text) -{ - if (strlen(text.text) == 1) - { - const char key = text.text[0]; - switch (key) { - case 0x20 ... 0x40: - case 0x5b ... 0x60: - case 0x7b ... 0x7e: - { - // not the letters - // this is very simple, but one cannot handle CRTL-key combination. - addKeyToBuffer(key); - std::cerr << "SDL TextInputEvent: " << std::hex << (int)key << std::dec << std::endl; - break; - } - } - } -} diff --git a/source/frontends/retro/emulator.h b/source/frontends/retro/emulator.h deleted file mode 100644 index 265ee440..00000000 --- a/source/frontends/retro/emulator.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include - -#include "frontends/common2/speed.h" - -class Emulator -{ -public: - Emulator( - const std::shared_ptr & window, - const std::shared_ptr & renderer, - const std::shared_ptr & texture, - const bool fixedSpeed - ); - - void execute(const size_t milliseconds); - - void updateTexture(); - void refreshVideo(); - - void processEvents(bool & quit); - -private: - void processKeyDown(const SDL_KeyboardEvent & key, bool & quit); - void processKeyUp(const SDL_KeyboardEvent & key); - void processText(const SDL_TextInputEvent & text); - - const std::shared_ptr myWindow; - const std::shared_ptr myRenderer; - const std::shared_ptr myTexture; - - int myMultiplier; - bool myFullscreen; - - Speed mySpeed; - - SDL_Rect myRect; - int myPitch; -};