PSP2: Initial support
Jeffrey Pfau jeffrey@endrift.com
Sat, 04 Jul 2015 14:42:09 -0700
10 files changed,
401 insertions(+),
1 deletions(-)
M
CMakeLists.txt
→
CMakeLists.txt
@@ -192,7 +192,7 @@ add_definitions(-D_DARWIN_C_SOURCE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.6") endif() -if(NOT HAIKU AND NOT MSVC) +if(NOT HAIKU AND NOT MSVC AND NOT PSP2) list(APPEND OS_LIB m) endif()@@ -450,6 +450,10 @@ add_executable(${BINARY_NAME}-perf ${PERF_SRC})
target_link_libraries(${BINARY_NAME}-perf ${BINARY_NAME} ${PERF_LIB}) install(TARGETS ${BINARY_NAME}-perf DESTINATION bin COMPONENT ${BINARY_NAME}-perf) install(FILES ${CMAKE_SOURCE_DIR}/tools/perf.py DESTINATION "${CMAKE_INSTALL_LIBDIR}/${BINARY_NAME}" COMPONENT ${BINARY_NAME}-perf) +endif() + +if(PSP2) + add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/psp2 ${CMAKE_BINARY_DIR}/psp2) endif() # Packaging
M
src/gba/hardware.c
→
src/gba/hardware.c
@@ -8,6 +8,10 @@
#include "gba/serialize.h" #include "util/hash.h" +#ifdef PSP2 +#include <psp2/rtc.h> +#endif + static void _readPins(struct GBACartridgeHardware* hw); static void _outputPins(struct GBACartridgeHardware* hw, unsigned pins);@@ -258,6 +262,10 @@ }
struct tm date; #ifdef _WIN32 date = *localtime(&t); +#elif defined(PSP2) + SceRtcTime scertc; + sceRtcGetCurrentClockLocalTime(&scertc); + sceRtcGetTime_t(&scertc, &t); #else localtime_r(&t, &date); #endif
A
src/platform/psp2/CMakeLists.txt
@@ -0,0 +1,8 @@
+file(GLOB PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/psp2/*.c) + +set(PLATFORM_LIBRARY -lSceCtrl_stub -lSceRtc_stub -lSceGxm_stub -lSceDisplay_stub -lm_stub) + +add_executable(${BINARY_NAME}.elf ${PLATFORM_SRC}) +target_link_libraries(${BINARY_NAME}.elf ${BINARY_NAME} ${PLATFORM_LIBRARY}) +set_target_properties(${BINARY_NAME}.elf PROPERTIES OUTPUT_NAME ${BINARY_NAME}.elf) +add_custom_command(TARGET ${BINARY_NAME}.elf POST_BUILD COMMAND ${FIXUP} -q -S ${BINARY_NAME}.elf ${BINARY_NAME}.velf MAIN_DEPENDENCY ${BINARY_NAME}.elf)
A
src/platform/psp2/CMakeToolchain.txt
@@ -0,0 +1,44 @@
+if(DEFINED ENV{DEVKITPRO}) + set(DEVKITPRO $ENV{DEVKITPRO}) +else() + message(FATAL_ERROR "Could not find DEVKITPRO in environment") +endif() + +if(DEFINED ENV{DEVKITARM}) + set(DEVKITARM $ENV{DEVKITARM}) +else() + set(DEVKITARM ${DEVKITPRO}/devkitARM) +endif() + +set(toolchain_dir ${DEVKITARM}/psp2) +set(toolchain_bin_dir ${toolchain_dir}/bin) +set(cross_prefix ${DEVKITARM}/bin/arm-none-eabi-) +set(inc_flags -I${toolchain_dir}/include) +set(arch_flags "-march=armv7-a -mtune=cortex-a9") +set(link_flags "-L${toolchain_dir}/lib -specs=psp2.specs ${arch_flags}") + +set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name") +set(CMAKE_SYSTEM_PROCESSOR arm CACHE INTERNAL "processor") +set(CMAKE_LIBRARY_ARCHITECTURE arm-none-eabi CACHE INTERNAL "abi") +set(CMAKE_AR ${cross_prefix}ar CACHE INTERNAL "archiver") +set(CMAKE_C_COMPILER ${cross_prefix}gcc CACHE INTERNAL "c compiler") +set(CMAKE_CXX_COMPILER ${cross_prefix}g++ CACHE INTERNAL "cxx compiler") +set(CMAKE_ASM_COMPILER ${cross_prefix}gcc CACHE INTERNAL "assembler") +set(common_flags "${arch_flags} -fno-reorder-functions -fno-optimize-sibling-calls ${inc_flags}") +set(CMAKE_C_FLAGS ${common_flags} CACHE INTERNAL "c compiler flags") +set(CMAKE_ASM_FLAGS ${common_flags} CACHE INTERNAL "c compiler flags") +set(CMAKE_CXX_FLAGS ${common_flags} CACHE INTERNAL "cxx compiler flags") +set(CMAKE_LINKER ${cross_prefix}ld CACHE INTERNAL "linker") + +set(CMAKE_EXE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "exe link flags") +set(CMAKE_MODULE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "module link flags") +set(CMAKE_SHARED_LINKER_FLAGS ${link_flags} CACHE INTERNAL "shared link flags") + +set(PKG_CONFIG_EXECUTABLE "/dev/null" CACHE INTERNAL "" FORCE) + +set(FIXUP ${toolchain_bin_dir}/psp2-fixup) + +set(PSP2 ON) +add_definitions(-DPSP2) + +set(CMAKE_C_COMPILER_WORKS 1) # Skip test
A
src/platform/psp2/main.c
@@ -0,0 +1,158 @@
+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "gba/gba.h" +#include "gba/video.h" + +#include "gba/renderers/video-software.h" +#include "util/memory.h" +#include "util/vfs.h" +#include "platform/psp2/sce-vfs.h" + +#include <psp2/ctrl.h> +#include <psp2/display.h> +#include <psp2/gxm.h> +#include <psp2/moduleinfo.h> +#include <psp2/kernel/memorymgr.h> +#include <psp2/kernel/processmgr.h> + +PSP2_MODULE_INFO(0, 0, "mGBA"); + +#define PSP2_HORIZONTAL_PIXELS 960 +#define PSP2_VERTICAL_PIXELS 544 + +static void allocFramebuffer(SceDisplayFrameBuf* fb, int nfbs, SceUID* memblock) { + size_t baseSize = 0x200000; + size_t size = baseSize * nfbs; + *memblock = sceKernelAllocMemBlock("fb", SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, size, 0); + sceKernelGetMemBlockBase(*memblock, &fb[0].base); + sceGxmMapMemory(fb[0].base, size, SCE_GXM_MEMORY_ATTRIB_RW); + + int i; + for (i = 0; i < nfbs; ++i) { + fb[i].size = sizeof(fb[i]); + fb[i].pitch = PSP2_HORIZONTAL_PIXELS; + fb[i].width = PSP2_HORIZONTAL_PIXELS; + fb[i].height = PSP2_VERTICAL_PIXELS; + fb[i].pixelformat = PSP2_DISPLAY_PIXELFORMAT_A8B8G8R8; + fb[i].base = (char*) fb[0].base + i * baseSize; + } +} + +int main() { + printf("%s initializing", projectName); + bool running = true; + + struct GBAVideoSoftwareRenderer renderer; + GBAVideoSoftwareRendererCreate(&renderer); + + struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA)); + struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore)); + + printf("GBA: %08X", gba); + printf("CPU: %08X", cpu); + int activeKeys = 0; + + SceGxmInitializeParams gxmParams; + gxmParams.flags = 0; + gxmParams.displayQueueMaxPendingCount = 2; + gxmParams.displayQueueCallback = 0; + gxmParams.displayQueueCallbackDataSize = 0; + gxmParams.parameterBufferSize = 0x1000000; + int ret = sceGxmInitialize(&gxmParams); + printf("sceGxmInitialize: %08X", ret); + + SceDisplayFrameBuf fb[2]; + int currentFb = 0; + SceUID memblock; + allocFramebuffer(fb, 2, &memblock); + printf("fb[0]: %08X", fb[0].base); + printf("fb[1]: %08X", fb[1].base); + + renderer.outputBuffer = fb[0].base; + renderer.outputBufferStride = PSP2_HORIZONTAL_PIXELS; + + struct VFile* rom = VFileOpenSce("cache0:/VitaDefilerClient/Documents/GBA/rom.gba", PSP2_O_RDONLY, 0666); + struct VFile* save = VFileOpenSce("cache0:/VitaDefilerClient/Documents/GBA/rom.sav", PSP2_O_RDWR | PSP2_O_CREAT, 0666); + + printf("ROM: %08X", rom); + printf("Save: %08X", save); + + GBACreate(gba); + ARMSetComponents(cpu, &gba->d, 0, 0); + ARMInit(cpu); + printf("%s initialized.", "CPU"); + + gba->keySource = &activeKeys; + gba->sync = 0; + + GBAVideoAssociateRenderer(&gba->video, &renderer.d); + + GBALoadROM(gba, rom, save, 0); + printf("%s loaded.", "ROM"); + + ARMReset(cpu); + + printf("%s all set and ready to roll.", projectName); + + int frameCounter = 0; + while (running) { + ARMRunLoop(cpu); + + if (frameCounter != gba->video.frameCounter) { + SceCtrlData pad; + sceCtrlPeekBufferPositive(0, &pad, 1); + activeKeys = 0; + if (pad.buttons & PSP2_CTRL_CROSS) { + activeKeys |= 1 << GBA_KEY_A; + } + if (pad.buttons & PSP2_CTRL_CIRCLE) { + activeKeys |= 1 << GBA_KEY_B; + } + if (pad.buttons & PSP2_CTRL_START) { + activeKeys |= 1 << GBA_KEY_START; + } + if (pad.buttons & PSP2_CTRL_SELECT) { + activeKeys |= 1 << GBA_KEY_SELECT; + } + if (pad.buttons & PSP2_CTRL_UP) { + activeKeys |= 1 << GBA_KEY_UP; + } + if (pad.buttons & PSP2_CTRL_DOWN) { + activeKeys |= 1 << GBA_KEY_DOWN; + } + if (pad.buttons & PSP2_CTRL_LEFT) { + activeKeys |= 1 << GBA_KEY_LEFT; + } + if (pad.buttons & PSP2_CTRL_RIGHT) { + activeKeys |= 1 << GBA_KEY_RIGHT; + } + if (pad.buttons & PSP2_CTRL_LTRIGGER) { + activeKeys |= 1 << GBA_KEY_L; + } + if (pad.buttons & PSP2_CTRL_RTRIGGER) { + activeKeys |= 1 << GBA_KEY_R; + } + + sceDisplaySetFrameBuf(&fb[currentFb], PSP2_DISPLAY_SETBUF_NEXTFRAME); + sceDisplayWaitVblankStart(); + currentFb = !currentFb; + renderer.outputBuffer = fb[currentFb].base; + + frameCounter = gba->video.frameCounter; + } + } + printf("%s shutting down...", projectName); + + ARMDeinit(cpu); + GBADestroy(gba); + + rom->close(rom); + save->close(save); + + mappedMemoryFree(gba, 0); + mappedMemoryFree(cpu, 0); + return 0; +}
A
src/platform/psp2/memory.c
@@ -0,0 +1,53 @@
+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "util/memory.h" +#include "util/vector.h" + +#include <psp2/kernel/memorymgr.h> +#include <psp2/types.h> + +DECLARE_VECTOR(SceUIDList, SceUID); +DEFINE_VECTOR(SceUIDList, SceUID); + +static struct SceUIDList uids; +static bool listInit = false; + +void* anonymousMemoryMap(size_t size) { + if (!listInit) { + SceUIDListInit(&uids, 8); + } + if (size & 0xFFF) { + // Align to 4kB pages + size += ((~size) & 0xFFF) + 1; + } + SceUID memblock = sceKernelAllocMemBlock("anon", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW, size, 0); + if (memblock < 0) { + return 0; + } + *SceUIDListAppend(&uids) = memblock; + void* ptr; + if (sceKernelGetMemBlockBase(memblock, &ptr) < 0) { + return 0; + } + return ptr; +} + +void mappedMemoryFree(void* memory, size_t size) { + UNUSED(size); + size_t i; + for (i = 0; i < SceUIDListSize(&uids); ++i) { + SceUID uid = *SceUIDListGetPointer(&uids, i); + void* ptr; + if (sceKernelGetMemBlockBase(uid, &ptr) < 0) { + continue; + } + if (ptr == memory) { + sceKernelFreeMemBlock(uid); + SceUIDListUnshift(&uids, i, 1); + return; + } + } +}
A
src/platform/psp2/sce-vfs.c
@@ -0,0 +1,99 @@
+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "sce-vfs.h" + +#include "util/vfs.h" +#include "util/memory.h" + + +struct VFileSce { + struct VFile d; + + SceUID fd; +}; + +static bool _vfsceClose(struct VFile* vf); +static off_t _vfsceSeek(struct VFile* vf, off_t offset, int whence); +static ssize_t _vfsceRead(struct VFile* vf, void* buffer, size_t size); +static ssize_t _vfsceWrite(struct VFile* vf, const void* buffer, size_t size); +static void* _vfsceMap(struct VFile* vf, size_t size, int flags); +static void _vfsceUnmap(struct VFile* vf, void* memory, size_t size); +static void _vfsceTruncate(struct VFile* vf, size_t size); +static ssize_t _vfsceSize(struct VFile* vf); + +struct VFile* VFileOpenSce(const char* path, int flags, SceMode mode) { + struct VFileSce* vfsce = malloc(sizeof(struct VFileSce)); + if (!vfsce) { + return 0; + } + + vfsce->fd = sceIoOpen(path, flags, mode); + if (vfsce->fd < 0) { + free(vfsce); + return 0; + } + + vfsce->d.close = _vfsceClose; + vfsce->d.seek = _vfsceSeek; + vfsce->d.read = _vfsceRead; + vfsce->d.readline = 0; + vfsce->d.write = _vfsceWrite; + vfsce->d.map = _vfsceMap; + vfsce->d.unmap = _vfsceUnmap; + vfsce->d.truncate = _vfsceTruncate; + vfsce->d.size = _vfsceSize; + + return &vfsce->d; +} + +bool _vfsceClose(struct VFile* vf) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + + return sceIoClose(vfsce->fd) >= 0; +} + +off_t _vfsceSeek(struct VFile* vf, off_t offset, int whence) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + return sceIoLseek(vfsce->fd, offset, whence); +} + +ssize_t _vfsceRead(struct VFile* vf, void* buffer, size_t size) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + return sceIoRead(vfsce->fd, buffer, size); +} + +ssize_t _vfsceWrite(struct VFile* vf, const void* buffer, size_t size) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + return sceIoWrite(vfsce->fd, buffer, size); +} + +static void* _vfsceMap(struct VFile* vf, size_t size, int flags) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + UNUSED(flags); + void* buffer = anonymousMemoryMap(size); + if (buffer) { + sceIoRead(vfsce->fd, buffer, size); + } + return buffer; +} + +static void _vfsceUnmap(struct VFile* vf, void* memory, size_t size) { + UNUSED(vf); + mappedMemoryFree(memory, size); +} + +static void _vfsceTruncate(struct VFile* vf, size_t size) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + // TODO +} + +ssize_t _vfsceSize(struct VFile* vf) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + SceOff cur = sceIoLseek(vfsce->fd, 0, SEEK_CUR); + SceOff end = sceIoLseek(vfsce->fd, 0, SEEK_END); + sceIoLseek(vfsce->fd, cur, SEEK_SET); + return end; +}
A
src/platform/psp2/sce-vfs.h
@@ -0,0 +1,18 @@
+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef SCE_VFS_H +#define SCE_VFS_H + +#ifdef PSP2 +#include <psp2/types.h> +#include <psp2/io/fcntl.h> +#else +#include <pspiofilemgr.h> +#endif + +struct VFile* VFileOpenSce(const char* path, int flags, SceMode mode); + +#endif
M
src/util/common.h
→
src/util/common.h
@@ -6,7 +6,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef COMMON_H #define COMMON_H +#ifndef PSP2 #include <ctype.h> +#endif #include <fcntl.h> #include <inttypes.h> #include <limits.h>
M
src/util/vfs.c
→
src/util/vfs.c
@@ -5,6 +5,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "vfs.h" +#ifdef PSP2 +#include "platform/psp2/sce-vfs.h" +#endif + struct VFile* VFileOpen(const char* path, int flags) { #ifdef USE_VFS_FILE const char* chflags;@@ -30,6 +34,8 @@ chflags = "rb";
break; } return VFileFOpen(path, chflags); +#elif defined(PSP2) + return VFileOpenSce(path, flags, 0666); #else return VFileOpenFD(path, flags); #endif