Unverified Commit 51ebc2ef authored by Libretro-Admin's avatar Libretro-Admin Committed by GitHub
Browse files

Merge pull request #8 from phcoder/psp

Add PSP and WiiU support
parents 9e5d641a c25da37f
Pipeline #92132 passed with stages
in 8 minutes and 52 seconds
......@@ -52,6 +52,10 @@ include:
file: '/ios9.yml'
################################## CONSOLES ################################
# PlayStation Portable
- project: 'libretro-infrastructure/ci-templates'
file: '/psp-static.yml'
# PlayStation Vita
- project: 'libretro-infrastructure/ci-templates'
file: '/vita-static.yml'
......@@ -68,6 +72,10 @@ include:
#- project: 'libretro-infrastructure/ci-templates'
#file: '/wii-static.yml'
# Nintendo WiiU
- project: 'libretro-infrastructure/ci-templates'
file: '/wiiu-static.yml'
# Nintendo Switch
- project: 'libretro-infrastructure/ci-templates'
file: '/libnx-static.yml'
......@@ -180,6 +188,12 @@ libretro-build-tvos-arm64:
- .core-defs
################################### CONSOLES #################################
# PlayStation Portable
libretro-build-psp:
extends:
- .libretro-psp-static-retroarch-master
- .core-defs
# Nintendo 3DS
libretro-build-ctr:
extends:
......@@ -198,6 +212,12 @@ libretro-build-ctr:
#- .libretro-wii-static-retroarch-master
#- .core-defs
# Nintendo WiiU
libretro-build-wiiu:
extends:
- .libretro-wiiu-static-retroarch-master
- .core-defs
# Nintendo Switch
libretro-build-libnx-aarch64:
extends:
......
......@@ -159,6 +159,16 @@ else ifeq ($(platform), libnx)
CFLAGS += -march=armv8-a -mtune=cortex-a57 -mtp=soft -mcpu=cortex-a57+crc+fp+simd -ffast-math
CXXFLAGS := $(ASFLAGS) $(CFLAGS)
STATIC_LINKING = 1
# Nintendo WiiU
else ifeq ($(platform), wiiu)
TARGET := $(TARGET_NAME)_libretro_$(platform).a
CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
CXX = $(DEVKITPPC)/bin/powerpc-eabi-g++$(EXE_EXT)
AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)
CFLAGS += -DGEKKO -DWIIU -DHW_RVL -mwup -mcpu=750 -meabi -mhard-float
CXXFLAGS += -DGEKKO -DWIIU -DHW_RVL -mwup -mcpu=750 -meabi -mhard-float
HAVE_RZLIB := 1
STATIC_LINKING=1
else ifeq ($(platform), retrofw)
EXT ?= so
TARGET := $(TARGET_NAME)_libretro.$(EXT)
......@@ -198,6 +208,15 @@ else ifeq ($(platform), vita)
AR = arm-vita-eabi-ar
CXXFLAGS += -Wl,-q -Wall -O3
STATIC_LINKING = 1
# PSP
else ifeq ($(platform), psp1)
TARGET := $(TARGET_NAME)_libretro_$(platform).a
CC = psp-gcc
CXX = psp-g++
AR = psp-ar
CFLAGS += -G0 -DPSP -DUSE_RGB565
CXXFLAGS += -G0 -DPSP -DUSE_RGB565
STATIC_LINKING=1
# CTR/3DS
else ifeq ($(platform), ctr)
TARGET := $(TARGET_NAME)_libretro_$(platform).a
......
......@@ -173,6 +173,7 @@ void Loader::load(const std::vector<std::string>& lines, Machine& m)
++fy;
break;
}
#if SOUND_ENABLED
case State::SFX:
{
assert(line.length() == DIGITS_PER_SOUND);
......@@ -234,6 +235,7 @@ void Loader::load(const std::vector<std::string>& lines, Machine& m)
break;
}
#endif
}
}
......
......@@ -165,8 +165,10 @@ void Stegano::load20(const PngData& data, Machine& m)
auto* d = data.data;
size_t o = RAW_DATA_LENGTH + MAGIC_LENGTH;
size_t decompressedLength = assembleByte(d[o++]) << 8 | assembleByte(d[o++]);
size_t compressedLength = assembleByte(d[o++]) << 8 | assembleByte(d[o++]);
size_t decompressedLength = assembleByte(d[o]) << 8 | assembleByte(d[o+1]);
size_t compressedLength = assembleByte(d[o+2]) << 8 | assembleByte(d[o+3]);
o += 4;
compressedLength -= HEADER_20_LENGTH; /* subtract header length */
compressedLength = std::min(size_t(32769ULL - RAW_DATA_LENGTH), compressedLength);
......@@ -255,6 +257,7 @@ void Stegano::load(const PngData& data, Machine& m)
constexpr size_t SPRITE_SHEET_SIZE = gfx::SPRITE_SHEET_HEIGHT * gfx::SPRITE_SHEET_WIDTH / gfx::PIXEL_TO_BYTE_RATIO;
constexpr size_t TILE_MAP_SIZE = gfx::TILE_MAP_WIDTH * gfx::TILE_MAP_HEIGHT * sizeof(sprite_index_t) / 2;
constexpr size_t SPRITE_FLAGS_SIZE = gfx::SPRITE_COUNT * sizeof(sprite_flags_t);
#if SOUND_ENABLED
constexpr size_t MUSIC_SIZE = sfx::MUSIC_COUNT * sizeof(sfx::music_t);
constexpr size_t SOUND_SIZE = sfx::SOUND_COUNT * sizeof(sfx::sound_t);
......@@ -262,6 +265,7 @@ void Stegano::load(const PngData& data, Machine& m)
static_assert(sizeof(sfx::sound_t) == 68, "Must be 68 bytes");
static_assert(RAW_DATA_LENGTH == SPRITE_SHEET_SIZE + TILE_MAP_SIZE + SPRITE_FLAGS_SIZE + MUSIC_SIZE + SOUND_SIZE, "Must be equal");
#endif
assert(data.length == IMAGE_WIDTH * IMAGE_HEIGHT);
auto* d = data.data;
......
......@@ -8,24 +8,82 @@
#include "vm/machine.h"
#include "vm/input.h"
#include <stdio.h>
#include <cstdarg>
#include <cstring>
#define LIBRETRO_LOG(x, ...) env.logger(retro_log_level::RETRO_LOG_INFO, x # __VA_ARGS__)
namespace r8 = retro8;
using pixel_t = uint32_t;
constexpr int SAMPLE_RATE = 44100;
constexpr int SAMPLES_PER_FRAME = SAMPLE_RATE / 60;
constexpr int SOUND_CHANNELS = 2;
r8::Machine machine;
r8::Machine *machine;
r8::io::Loader loader;
r8::input::InputManager input;
r8::gfx::ColorTable colorTable;
pixel_t* screen;
template <typename pixel_t>
class Screen {
public:
~Screen() {
delete[] buffer;
}
void draw(const r8::gfx::color_byte_t *data, r8::gfx::palette_t *palette) {
auto pointer = buffer;
for (size_t i = 0; i < r8::gfx::BYTES_PER_SCREEN; ++i) {
const r8::gfx::color_byte_t* pixels = data + i;
const auto rc1 = colorTable.get(palette->get((pixels)->low()));
const auto rc2 = colorTable.get(palette->get((pixels)->high()));
*(pointer) = rc1;
*((pointer)+1) = rc2;
(pointer) += 2;
}
}
const pixel_t *getBuffer() {
return buffer;
}
protected:
Screen(): buffer(new pixel_t[r8::gfx::SCREEN_WIDTH * r8::gfx::SCREEN_HEIGHT]) {
}
protected:
r8::gfx::ColorTable colorTable;
private:
pixel_t *buffer;
};
struct Screen32 : Screen<uint32_t> {
public:
Screen32() {
colorTable.init(ColorMapper32());
}
private:
struct ColorMapper32
{
r8::gfx::ColorTable::pixel_t operator()(uint8_t r, uint8_t g, uint8_t b) const { return 0xff000000 | (r << 16) | (g << 8) | b; }
};
};
struct Screen16 : Screen<uint16_t> {
public:
Screen16() {
colorTable.init(ColorMapper16());
}
private:
struct ColorMapper16
{
r8::gfx::ColorTable::pixel_t operator()(uint8_t r, uint8_t g, uint8_t b) const { return ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | ((b & 0xf8) >> 3); }
};
};
Screen16 *screen16;
Screen32 *screen32;
int16_t* audioBuffer;
static void fallback_log(enum retro_log_level level, const char *fmt, ...)
......@@ -45,21 +103,38 @@ struct RetroArchEnv
retro_input_poll_t inputPoll;
retro_input_state_t inputState;
retro_log_printf_t logger = fallback_log;
retro_environment_t retro_cb;
uint32_t frameCounter;
uint16_t buttonState;
bool isRGB32;
};
RetroArchEnv env;
struct ColorMapper
{
r8::gfx::ColorTable::pixel_t operator()(uint8_t r, uint8_t g, uint8_t b) const { return 0xff000000 | (r << 16) | (g << 8) | b; }
};
//TODO
uint32_t Platform::getTicks() { return 0; }
static bool tryScreen32() {
retro_pixel_format pixelFormat = RETRO_PIXEL_FORMAT_XRGB8888;
if (!env.retro_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &pixelFormat))
return false;
env.isRGB32 = true;
screen32 = new Screen32();
env.logger(retro_log_level::RETRO_LOG_INFO, "Initializing XRGB8888 screen buffer of %d bytes\n", 4*r8::gfx::SCREEN_WIDTH*r8::gfx::SCREEN_HEIGHT);
return true;
}
static bool tryScreen16() {
retro_pixel_format pixelFormat = RETRO_PIXEL_FORMAT_RGB565;
if (!env.retro_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &pixelFormat))
return false;
env.isRGB32 = false;
screen16 = new Screen16();
env.logger(retro_log_level::RETRO_LOG_INFO, "Initializing RGB565 screen buffer of %d bytes\n", 2*r8::gfx::SCREEN_WIDTH*r8::gfx::SCREEN_HEIGHT);
return true;
}
extern "C"
{
unsigned retro_api_version()
......@@ -69,21 +144,12 @@ extern "C"
void retro_init()
{
screen = new pixel_t[r8::gfx::SCREEN_WIDTH * r8::gfx::SCREEN_HEIGHT];
env.logger(retro_log_level::RETRO_LOG_INFO, "Initializing screen buffer of %d bytes\n", sizeof(pixel_t)*r8::gfx::SCREEN_WIDTH*r8::gfx::SCREEN_HEIGHT);
audioBuffer = new int16_t[SAMPLE_RATE * 2];
env.logger(retro_log_level::RETRO_LOG_INFO, "Initializing audio buffer of %d bytes\n", sizeof(int16_t) * SAMPLE_RATE * 2);
colorTable.init(ColorMapper());
machine.font().load();
machine.code().loadAPI();
input.setMachine(&machine);
}
void retro_deinit()
{
delete[] screen;
delete[] audioBuffer;
//TODO: release all structures bound to Lua etc
}
......@@ -114,8 +180,7 @@ extern "C"
void retro_set_environment(retro_environment_t e)
{
retro_pixel_format pixelFormat = RETRO_PIXEL_FORMAT_XRGB8888;
e(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &pixelFormat);
env.retro_cb = e;
retro_log_callback logger;
if (e(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &logger))
......@@ -142,6 +207,11 @@ extern "C"
bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info) { return false; }
bool retro_load_game(const retro_game_info* info)
{
machine = new r8::Machine();
machine->font().load();
machine->code().loadAPI();
input.setMachine(machine);
if (info && info->data)
{
input.reset();
......@@ -157,33 +227,53 @@ extern "C"
std::vector<uint8_t> out;
unsigned long width, height;
auto result = Platform::loadPNG(out, width, height, (uint8_t*)bdata, info->size, true);
int result = Platform::loadPNG(out, width, height, (uint8_t*)bdata, info->size, true);
assert(result == 0);
r8::io::Stegano stegano;
stegano.load({ reinterpret_cast<const uint32_t*>(out.data()), nullptr, out.size() / 4 }, machine);
uint32_t *buf = new uint32_t[out.size() / 4];
for (int i = 0; i < out.size() / 4; i++) {
buf[i] = out[4 * i] | (out[4 * i + 1] << 8) | (out[4 * i + 2] << 16) | (out[4 * i + 3] << 24);
}
r8::io::PngData pngData = { buf, NULL, out.size() / 4 };
stegano.load(pngData, *machine);
delete[] buf;
}
else
{
//TODO: not efficient since it's copied and it's not checking for '\0'
std::string raw(bdata);
loader.loadRaw(raw, machine);
loader.loadRaw(raw, *machine);
}
machine.memory().backupCartridge();
machine->memory().backupCartridge();
if (machine.code().hasInit())
if (machine->code().hasInit())
{
//_initFuture = std::async(std::launch::async, []() {
LIBRETRO_LOG("[Retro8] Cartridge has _init() function, calling it.");
machine.code().init();
machine->code().init();
LIBRETRO_LOG("[Retro8] _init() function completed execution.");
//});
}
machine.sound().init();
#if SOUND_ENABLED
machine->sound().init();
#endif
env.frameCounter = 0;
screen16 = NULL;
screen32 = NULL;
#ifdef USE_RGB565
if (!tryScreen16() && !tryScreen32())
#else
if (!tryScreen32() && !tryScreen16())
#endif
{
env.logger(retro_log_level::RETRO_LOG_ERROR, "Couldn't find compatible pixel format\n");
return false;
}
return true;
}
......@@ -193,41 +283,44 @@ extern "C"
void retro_unload_game(void)
{
/* TODO */
if (screen16)
delete screen16;
if (screen32)
delete screen32;
screen16 = NULL;
screen32 = NULL;
delete machine;
}
void retro_run()
{
/* if code is at 60fps or every 2 frames (30fps) */
if (machine.code().require60fps() || env.frameCounter % 2 == 0)
if (machine->code().require60fps() || env.frameCounter % 2 == 0)
{
/* call _update and _draw of PICO-8 code */
machine.code().update();
machine.code().draw();
machine->code().update();
machine->code().draw();
/* rasterize screen memory to ARGB framebuffer */
auto* data = machine.memory().screenData();
auto* screenPalette = machine.memory().paletteAt(retro8::gfx::SCREEN_PALETTE_INDEX);
auto pointer = screen;
auto* data = machine->memory().screenData();
auto* screenPalette = machine->memory().paletteAt(retro8::gfx::SCREEN_PALETTE_INDEX);
for (size_t i = 0; i < r8::gfx::BYTES_PER_SCREEN; ++i)
{
const r8::gfx::color_byte_t* pixels = data + i;
const auto rc1 = colorTable.get(screenPalette->get((pixels)->low()));
const auto rc2 = colorTable.get(screenPalette->get((pixels)->high()));
*(pointer) = rc1;
*((pointer)+1) = rc2;
(pointer) += 2;
}
if (env.isRGB32)
screen32->draw(data, screenPalette);
else
screen16->draw(data, screenPalette);
input.manageKeyRepeat();
}
env.video(screen, r8::gfx::SCREEN_WIDTH, r8::gfx::SCREEN_HEIGHT, r8::gfx::SCREEN_WIDTH * sizeof(pixel_t));
if (env.isRGB32)
env.video(screen32->getBuffer(), r8::gfx::SCREEN_WIDTH, r8::gfx::SCREEN_HEIGHT, r8::gfx::SCREEN_WIDTH * sizeof(uint32_t));
else
env.video(screen16->getBuffer(), r8::gfx::SCREEN_WIDTH, r8::gfx::SCREEN_HEIGHT, r8::gfx::SCREEN_WIDTH * sizeof(uint16_t));
++env.frameCounter;
machine.sound().renderSounds(audioBuffer, SAMPLES_PER_FRAME);
#if SOUND_ENABLED
machine->sound().renderSounds(audioBuffer, SAMPLES_PER_FRAME);
/* duplicate channels */
auto* audioBuffer2 = audioBuffer + SAMPLE_RATE;
......@@ -238,6 +331,10 @@ extern "C"
}
env.audioBatch(audioBuffer2, SAMPLES_PER_FRAME);
#else
memset(audioBuffer, 0, sizeof(audioBuffer[0]) * 2 * SAMPLES_PER_FRAME);
env.audioBatch(audioBuffer, SAMPLES_PER_FRAME);
#endif
/* manage input */
{
......
#include "views/view_manager.h"
#include "views/main_view.h"
#include "vm/machine.h"
/*
* D-PAD Left - SDLK_LEFT
......@@ -28,6 +29,7 @@ extern int testMain(int argc, char* argv[]);
int main(int argc, char* argv[])
{
machine = new retro8::Machine();
#if TEST_MODE
return testMain(argc, argv);
#endif
......
......@@ -15,8 +15,7 @@
using namespace retro8;
using namespace retro8::gfx;
extern retro8::Machine machine;
Machine& m = machine;
Machine& m = *machine;
TEST_CASE("cursor([x,] [y,] [col])")
{
......
......@@ -69,8 +69,7 @@ SDLAudio sdlAudio;
using namespace ui;
namespace r8 = retro8;
retro8::Machine machine;
retro8::Machine *machine;
GameView::GameView(ViewManager* manager) : manager(manager),
_paused(false), _showFPS(false), _showCartridgeName(false)
......@@ -80,8 +79,8 @@ _paused(false), _showFPS(false), _showCartridgeName(false)
void GameView::update()
{
machine.code().update();
machine.code().draw();
machine->code().update();
machine->code().draw();
}
......@@ -138,8 +137,8 @@ struct ColorMapper
void GameView::rasterize()
{
auto* data = machine.memory().screenData();
auto* screenPalette = machine.memory().paletteAt(r8::gfx::SCREEN_PALETTE_INDEX);
auto* data = machine->memory().screenData();
auto* screenPalette = machine->memory().paletteAt(r8::gfx::SCREEN_PALETTE_INDEX);
uint32_t* output = _output.pixels();
for (size_t i = 0; i < r8::gfx::BYTES_PER_SCREEN; ++i)
......@@ -155,7 +154,6 @@ void GameView::rasterize()
}
bool init = false;
void GameView::render()
{
......@@ -181,8 +179,8 @@ void GameView::render()
_frameCounter = 0;
machine.code().loadAPI();
_input.setMachine(&machine);
machine->code().loadAPI();
_input.setMachine(machine);
if (_path.empty())
......@@ -193,7 +191,7 @@ void GameView::render()
auto cartridge = loadPng(_path);
retro8::io::Stegano stegano;
stegano.load(cartridge, machine);
stegano.load(cartridge, *machine);
manager->setPngCartridge(static_cast<SDL_Surface*>(cartridge.userData));
SDL_FreeSurface(static_cast<SDL_Surface*>(cartridge.userData));
......@@ -201,27 +199,27 @@ void GameView::render()
else
{
r8::io::Loader loader;
loader.loadFile(_path, machine);
loader.loadFile(_path, *machine);
manager->setPngCartridge(nullptr);
}
machine.memory().backupCartridge();
machine->memory().backupCartridge();
int32_t fps = machine.code().require60fps() ? 60 : 30;
int32_t fps = machine->code().require60fps() ? 60 : 30;
manager->setFrameRate(fps);
if (machine.code().hasInit())
if (machine->code().hasInit())
{
/* init is launched on a different thread because some developers are using busy loops and manual flips */
_initFuture = std::async(std::launch::async, []() {
LOGD("Cartridge has _init() function, calling it.");
machine.code().init();
machine->code().init();
LOGD("_init() function completed execution.");
});
}
machine.sound().init();
sdlAudio.init(&machine.sound());
machine->sound().init();
sdlAudio.init(&machine->sound());
sdlAudio.resume();
init = true;
......@@ -259,7 +257,7 @@ void GameView::render()
if (_showFPS)
{
char buffer[16];
sprintf(buffer, "%.0f/%c0", 1000.0f / manager->lastFrameTicks(), machine.code().require60fps() ? '6' : '3');
sprintf(buffer, "%.0f/%c0", 1000.0f / manager->lastFrameTicks(), machine->code().require60fps() ? '6' : '3');
manager->text(buffer, 10, 10);
}
......@@ -276,7 +274,7 @@ void GameView::render()
for (r8::coord_t y = 0; y < r8::gfx::SPRITE_SHEET_HEIGHT; ++y)
for (r8::coord_t x = 0; x < r8::gfx::SPRITE_SHEET_PITCH; ++x)
{
const r8::gfx::color_byte_t* data = machine.memory().as<r8::gfx::color_byte_t>(r8::address::SPRITE_SHEET + y * r8::gfx::SPRITE_SHEET_PITCH + x);
const r8::gfx::color_byte_t* data = machine->memory().as<r8::gfx::color_byte_t>(r8::address::SPRITE_SHEET + y * r8::gfx::SPRITE_SHEET_PITCH + x);
RASTERIZE_PIXEL_PAIR(machine, dest, data);
}
......@@ -296,7 +294,7 @@ void GameView::render()
for (r8::palette_index_t j = 0; j < 2; ++j)
{
const r8::gfx::palette_t* palette = machine.memory().paletteAt(j);
const r8::gfx::palette_t* palette = machine->memory().paletteAt(j);
for (size_t i = 0; i < r8::gfx::COLOR_COUNT; ++i)
dest[j*16 + i] = colorTable.get(palette->get(r8::color_t(i)));
......@@ -323,13 +321,13 @@ void GameView::render()
{
for (r8::coord_t tx = 0; tx < r8::gfx::TILE_MAP_WIDTH; ++tx)
{
r8::sprite_index_t index = *machine.memory().spriteInTileMap(tx, ty);
r8::sprite_index_t index = *machine->memory().spriteInTileMap(tx, ty);
for (r8::coord_t y = 0; y < r8::gfx::SPRITE_HEIGHT; ++y)
for (r8::coord_t x = 0; x < r8::gfx::SPRITE_WIDTH; ++x)
{
auto* dest = base + x + tx * r8::gfx::SPRITE_WIDTH + (y + ty * r8::gfx::SPRITE_HEIGHT) * tilemap->h;
const r8::gfx::color_byte_t& pixels = machine.memory().spriteAt(index)->byteAt(x, y);
const r8::gfx::color_byte_t& pixels = machine->memory().spriteAt(index)->byteAt(x, y);
RASTERIZE_PIXEL_PAIR(machine, dest, &pixels);
}
}
......@@ -384,9 +382,9 @@ void GameView::handleKeyboardEvent(const SDL_Event& event)
{
if (event.type == SDL_KEYDOWN)
{
bool s = machine.sound().isMusicEnabled();
machine.sound().toggleMusic(!s);
machine.sound().toggleSound(!s);
bool s = machine->sound().isMusicEnabled();
machine->sound().toggleMusic(!s);
machine->sound().toggleSound(!s);
}
break;
}
......
#include "main_view.h"
extern retro8::Machine machine;
using namespace ui;
struct MenuEntry
......@@ -65,15 +63,15 @@ MenuView::MenuView(ViewManager* gvm) : _gvm(gvm), _cartridge(nullptr)
};
optionsMenu[SOUND].lambda = [this]() {
bool v = !machine.sound().isSoundEnabled();
machine.sound().toggleSound(v);