diff options
| author | spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> | 2021-08-17 11:37:03 +0000 |
|---|---|---|
| committer | spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> | 2021-08-17 11:37:03 +0000 |
| commit | f2fc18f82dd7900465d6ab3ae2080726d5589d39 (patch) | |
| tree | 74bcf61c346cdc4f108b8295dd82eca421a0eb1d /examples/system | |
| parent | 89751f29467b359339a8c8b57c218cc3328bae43 (diff) | |
| download | psn00bsdk-f2fc18f82dd7900465d6ab3ae2080726d5589d39.tar.gz | |
Added dynamic linker API and example, updated README and changelog
Diffstat (limited to 'examples/system')
| -rw-r--r-- | examples/system/dynlink/display.c | 70 | ||||
| -rw-r--r-- | examples/system/dynlink/iso.xml | 30 | ||||
| -rw-r--r-- | examples/system/dynlink/library/ball16c.h | 16 | ||||
| -rw-r--r-- | examples/system/dynlink/library/balls.c | 116 | ||||
| -rw-r--r-- | examples/system/dynlink/library/cube.c | 154 | ||||
| -rw-r--r-- | examples/system/dynlink/library/dll_common.h | 30 | ||||
| -rw-r--r-- | examples/system/dynlink/main.c | 225 | ||||
| -rw-r--r-- | examples/system/dynlink/makefile | 95 | ||||
| -rw-r--r-- | examples/system/dynlink/system.cnf | 4 |
9 files changed, 740 insertions, 0 deletions
diff --git a/examples/system/dynlink/display.c b/examples/system/dynlink/display.c new file mode 100644 index 0000000..d8ad3ed --- /dev/null +++ b/examples/system/dynlink/display.c @@ -0,0 +1,70 @@ +/* + * PSn00bSDK dynamic linker example (utilities) + * (C) 2021 spicyjpeg - MPL licensed + */ + +#include <psxgpu.h> + +#include "library/dll_common.h" + +#define SCREEN_XRES 320 +#define SCREEN_YRES 240 + +/* Display/GPU context utilities */ + +void init_context(CONTEXT *ctx) { + DB *db; + + ResetGraph(0); + ctx->xres = SCREEN_XRES; + ctx->yres = SCREEN_YRES; + ctx->db_active = 0; + + db = &(ctx->db[0]); + SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES); + SetDefDrawEnv(&(db->draw), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); + setRGB0(&(db->draw), 63, 0, 127); + db->draw.isbg = 1; + db->draw.dtd = 1; + + db = &(ctx->db[1]); + SetDefDispEnv(&(db->disp), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); + SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES); + setRGB0(&(db->draw), 63, 0, 127); + db->draw.isbg = 1; + db->draw.dtd = 1; + + // Set up the ordering tables and primitive buffers. + db = &(ctx->db[0]); + ctx->db_nextpri = db->p; + ClearOTagR((u_long *) db->ot, OT_LEN); + + PutDrawEnv(&(db->draw)); + //PutDispEnv(&(db->disp)); + + db = &(ctx->db[1]); + ClearOTagR((u_long *) db->ot, OT_LEN); + + // Create a text stream at the top of the screen. + FntLoad(960, 0); + FntOpen(4, 12, 312, 32, 2, 256); +} + +void display(CONTEXT *ctx) { + DB *db; + + DrawSync(0); + VSync(0); + ctx->db_active ^= 1; + + db = &(ctx->db[ctx->db_active]); + ctx->db_nextpri = db->p; + ClearOTagR((u_long *) db->ot, OT_LEN); + + PutDrawEnv(&(db->draw)); + PutDispEnv(&(db->disp)); + SetDispMask(1); + + db = &(ctx->db[!ctx->db_active]); + DrawOTag((u_long *) &(db->ot[OT_LEN - 1])); +} diff --git a/examples/system/dynlink/iso.xml b/examples/system/dynlink/iso.xml new file mode 100644 index 0000000..76cc8fc --- /dev/null +++ b/examples/system/dynlink/iso.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<iso_project + image_name="build/dynlink.bin" + cue_sheet="build/dynlink.cue" +> + <track type="data"> + <identifiers + system ="PLAYSTATION" + volume ="DYNLINK" + volume_set ="DYNLINK" + publisher ="MEIDOTEK" + data_preparer ="PSN00BSDK BUILD SCRIPT" + application ="PLAYSTATION" + copyright ="README.TXT;1" + /> + + <directory_tree> + <file name="SYSTEM.CNF" type="data" source="system.cnf" /> + <file name="MAIN.EXE" type="data" source="build/main.exe" /> + <file name="MAIN.MAP" type="data" source="build/main.map" /> + + <file name="CUBE.DLL" type="data" source="build/library/cube.dll" /> + <file name="BALLS.DLL" type="data" source="build/library/balls.dll" /> + + <dummy sectors="1024"/> + </directory_tree> + </track> + + <!--<track type="audio" source="track2.wav" />--> +</iso_project> diff --git a/examples/system/dynlink/library/ball16c.h b/examples/system/dynlink/library/ball16c.h new file mode 100644 index 0000000..c79f273 --- /dev/null +++ b/examples/system/dynlink/library/ball16c.h @@ -0,0 +1,16 @@ +unsigned int ball16c_size=192; +unsigned char ball16c[] = { +0x10,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0xc0,0x03,0x10, +0x01,0x10,0x00,0x01,0x00,0x00,0x00,0x31,0xc6,0x73,0xce,0x94,0xd2,0x07,0x9d, +0xd6,0xda,0x38,0xe3,0xef,0xbd,0x9b,0xef,0x8c,0xb1,0xc6,0x98,0xde,0xfb,0x4a, +0xa9,0xa4,0x90,0xad,0xb5,0x00,0x00,0x8c,0x00,0x00,0x00,0xc0,0x03,0x00,0x01, +0x04,0x00,0x10,0x00,0x00,0x00,0x10,0x22,0x12,0x02,0x00,0x00,0x00,0x10,0x32, +0x33,0x23,0x11,0x04,0x00,0x00,0x23,0x55,0x66,0x35,0x72,0x47,0x00,0x20,0x52, +0x86,0x68,0x36,0x12,0x97,0x0a,0x20,0x65,0xbb,0x8b,0x36,0x12,0x91,0x04,0x31, +0x85,0xbb,0x68,0x35,0x12,0x97,0xdc,0x32,0x86,0x8b,0x56,0x35,0x73,0x97,0xa4, +0x32,0x66,0x68,0x55,0x23,0x71,0x9e,0xac,0x32,0x65,0x56,0x33,0x13,0x71,0xce, +0xa4,0x21,0x33,0x33,0x23,0x11,0xe7,0xc9,0xd4,0x12,0x22,0x22,0x13,0x71,0xe7, +0xc9,0xda,0x10,0x17,0x11,0x77,0x77,0x9e,0x4c,0x0d,0x40,0x77,0x71,0xe7,0x9e, +0xc9,0xd4,0x0d,0x00,0x94,0x99,0x99,0xcc,0x4c,0xda,0x00,0x00,0xa0,0xc4,0xc4, +0x44,0xda,0x0d,0x00,0x00,0x00,0xd0,0xaa,0xda,0x0d,0x00,0x00 +}; diff --git a/examples/system/dynlink/library/balls.c b/examples/system/dynlink/library/balls.c new file mode 100644 index 0000000..2a4d9f4 --- /dev/null +++ b/examples/system/dynlink/library/balls.c @@ -0,0 +1,116 @@ +/* + * PSn00bSDK dynamic linker example (DLL 2) + * (C) 2021 spicyjpeg - MPL licensed + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <psxgpu.h> +#include <psxgte.h> +#include <psxpad.h> +#include <inline_c.h> + +#include "dll_common.h" +#include "ball16c.h" + +/* Balls data */ + +typedef struct { + int16_t x, y; + int16_t xdir, ydir; + uint8_t r, g, b, p; +} BALL_TYPE; + +#define MAX_BALLS 512 + +/* Functions called by the main executable */ + +// NOTE: DLLs have no main(), _start() or other defined entry point. C++ global +// objects are automatically constructed when loading the library, and their +// destructors are called when unloading it via dlclose(). Other than that, the +// main executable can freely call DLL functions in any order, however it's +// still recommended (at least for C code) to have an init() function to e.g. +// initialize variables or hardware. + +static uint32_t frame = 0; +static BALL_TYPE balls[MAX_BALLS]; +static TIM_IMAGE ball_tim; + +void init(CONTEXT *ctx) { + GetTimInfo((u_long *) ball16c, &ball_tim); + + LoadImage(ball_tim.prect, ball_tim.paddr); + if (ball_tim.mode & 8) + LoadImage(ball_tim.crect, ball_tim.caddr); + + // Initialize the balls by giving them a random initial position, velocity + // and color. + for (uint32_t i = 0; i < MAX_BALLS; i++) { + BALL_TYPE *b = &(balls[i]); + + b->x = rand() % (ctx->xres - 16); + b->y = rand() % (ctx->yres - 16); + b->xdir = ((rand() & 1) ? 1 : -1) * ((rand() % 3) + 1); + b->ydir = ((rand() & 1) ? 1 : -1) * ((rand() % 3) + 1); + b->r = rand() & 0xff; + b->g = rand() & 0xff; + b->b = rand() & 0xff; + } +} + +void render(CONTEXT *ctx, uint16_t buttons) { + DB *db = &(ctx->db[ctx->db_active]); + SPRT_16 *sprt = (SPRT_16 *) ctx->db_nextpri; + + for (uint32_t i = 0; i < MAX_BALLS; i++) { + BALL_TYPE *b = &(balls[i]); + + setSprt16(sprt); + + setXY0(sprt, b->x, b->y); + setRGB0(sprt, b->r, b->g, b->b); + setUV0(sprt, 0, 0); + setClut(sprt, ball_tim.crect->x, ball_tim.crect->y); + + addPrim(&(db->ot[OT_LEN - 1]), sprt); + sprt++; + + // Update the ball's velocity and acceleration, moving them slower if + // cross is pressed. + int16_t step = !(buttons & PAD_CROSS) ? 1 : 0; + b->x += b->xdir >> step; + b->y += b->ydir >> step; + + if ( + (b->x < 0) || + ((b->x + 16) > ctx->xres) + ) + b->xdir *= -1; + if ( + (b->y < 0) || + ((b->y + 16) > ctx->yres) + ) + b->ydir *= -1; + } + + ctx->db_nextpri = (uint8_t *) sprt; + + // Add a TPAGE "primitive" to ensure the GPU finds the ball texture. + DR_TPAGE *tpri = (DR_TPAGE * ) ctx->db_nextpri; + + setDrawTPage( + tpri, + 0, + 0, + getTPage(0, 0, ball_tim.prect->x, ball_tim.prect->y) + ); + addPrim(&(db->ot[OT_LEN - 1]), tpri); + tpri++; + + ctx->db_nextpri = (uint8_t *) tpri; + + // Due to our custom resolver, this will actually call dll_printf() in the + // main executable. + printf("DRAWING BALLS, COUNTER=%d\n", frame++); +} diff --git a/examples/system/dynlink/library/cube.c b/examples/system/dynlink/library/cube.c new file mode 100644 index 0000000..57f3e56 --- /dev/null +++ b/examples/system/dynlink/library/cube.c @@ -0,0 +1,154 @@ +/* + * PSn00bSDK dynamic linker example (DLL 1) + * (C) 2021 spicyjpeg - MPL licensed + */ + +#include <sys/types.h> +#include <stdio.h> +#include <psxgpu.h> +#include <psxgte.h> +#include <psxpad.h> +#include <inline_c.h> + +#include "dll_common.h" + +/* Cube model */ + +typedef struct { + int16_t v0, v1, v2, v3; +} INDEX; + +static SVECTOR cube_verts[] = { + { -100, -100, -100, 0 }, + { 100, -100, -100, 0 }, + { -100, 100, -100, 0 }, + { 100, 100, -100, 0 }, + { 100, -100, 100, 0 }, + { -100, -100, 100, 0 }, + { 100, 100, 100, 0 }, + { -100, 100, 100, 0 } +}; +static SVECTOR cube_norms[] = { + { 0, 0, -ONE, 0 }, + { 0, 0, ONE, 0 }, + { 0, -ONE, 0, 0 }, + { 0, ONE, 0, 0 }, + { -ONE, 0, 0, 0 }, + { ONE, 0, 0, 0 } +}; +static INDEX cube_indices[] = { + { 0, 1, 2, 3 }, + { 4, 5, 6, 7 }, + { 5, 4, 0, 1 }, + { 6, 7, 3, 2 }, + { 0, 2, 5, 7 }, + { 3, 1, 6, 4 } +}; + +#define CUBE_FACES 6 + +/* Light matrices */ + +// Each column represents the color matrix of each light source and is used as +// material color when using gte_ncs() or multiplied by a source color when +// using gte_nccs(). 4096 is 1.0 in this matrix. A column of zeroes disables +// the light source. +static MATRIX color_mtx = { + ONE, 0, 0, // R + ONE, 0, 0, // G + ONE, 0, 0 // B +}; + +// Each row represents a vector direction of each light source. An entire row +// of zeroes disables the light source. +static MATRIX light_mtx = { + -2048, -2048, -2048, + 0, 0, 0, + 0, 0, 0 +}; + +/* Functions called by the main executable */ + +// NOTE: DLLs have no main(), _start() or other defined entry point. C++ global +// objects are automatically constructed when loading the library, and their +// destructors are called when unloading it via dlclose(). Other than that, the +// main executable can freely call DLL functions in any order, however it's +// still recommended (at least for C code) to have an init() function to e.g. +// initialize variables or hardware. + +static uint32_t frame = 0; +static SVECTOR rot = { 0 }; +static VECTOR pos = { 0, 0, 400 }; +static MATRIX mtx, lmtx; + +void init(CONTEXT *ctx) { + InitGeom(); + + gte_SetGeomOffset(ctx->xres / 2, ctx->yres / 2); + gte_SetGeomScreen(ctx->xres / 2); + gte_SetBackColor(63, 63, 63); + gte_SetColorMatrix(&color_mtx); +} + +void render(CONTEXT *ctx, uint16_t buttons) { + RotMatrix(&rot, &mtx); + TransMatrix(&mtx, &pos); + MulMatrix0(&light_mtx, &mtx, &lmtx); + + gte_SetRotMatrix(&mtx); + gte_SetTransMatrix(&mtx); + gte_SetLightMatrix(&lmtx); + + // Spin the cube faster is cross is pressed. + int16_t step = !(buttons & PAD_CROSS) ? 32 : 16; + rot.vx += step; + rot.vz += step; + + DB *db = &(ctx->db[ctx->db_active]); + POLY_F4 *pol4 = (POLY_F4 *) ctx->db_nextpri; + + for (uint32_t i = 0; i < CUBE_FACES; i++) { + int32_t p; + + gte_ldv3( + &cube_verts[cube_indices[i].v0], + &cube_verts[cube_indices[i].v1], + &cube_verts[cube_indices[i].v2] + ); + + gte_rtpt(); + gte_nclip(); + gte_stopz(&p); + if (p < 0) + continue; + + gte_avsz4(); + gte_stotz(&p); + if ((p >> 2) > OT_LEN) + continue; + + setPolyF4(pol4); + + gte_stsxy0(&(pol4->x0)); + gte_stsxy1(&(pol4->x1)); + gte_stsxy2(&(pol4->x2)); + + gte_ldv0(&(cube_verts[cube_indices[i].v3])); + gte_rtps(); + gte_stsxy(&(pol4->x3)); + + gte_ldrgb(&(pol4->r0)); + gte_ldv0(&(cube_norms[i])); + gte_ncs(); + gte_strgb(&(pol4->r0)); + + addPrim(&(db->ot[p >> 2]), pol4); + pol4++; + } + + ctx->db_nextpri = (uint8_t *) pol4; + + // Due to our custom resolver, this will actually call dll_printf() in the + // main executable. + printf("DRAWING CUBE, COUNTER=%d\n", frame++); +} diff --git a/examples/system/dynlink/library/dll_common.h b/examples/system/dynlink/library/dll_common.h new file mode 100644 index 0000000..4f9314b --- /dev/null +++ b/examples/system/dynlink/library/dll_common.h @@ -0,0 +1,30 @@ +/* + * PSn00bSDK dynamic linker example (shared header) + * (C) 2021 spicyjpeg - MPL licensed + */ + +#ifndef __DLL_COMMON_H +#define __DLL_COMMON_H + +#include <psxgpu.h> + +/* Common structures shared by the main executable and DLLs */ + +#define OT_LEN 256 +#define PACKET_LEN 16384 + +typedef struct { + DISPENV disp; + DRAWENV draw; + uint32_t ot[OT_LEN]; + uint8_t p[PACKET_LEN]; +} DB; + +typedef struct { + uint16_t xres, yres; + DB db[2]; + uint32_t db_active; + uint8_t *db_nextpri; +} CONTEXT; + +#endif diff --git a/examples/system/dynlink/main.c b/examples/system/dynlink/main.c new file mode 100644 index 0000000..70314da --- /dev/null +++ b/examples/system/dynlink/main.c @@ -0,0 +1,225 @@ +/* + * PSn00bSDK dynamic linker example (main executable) + * (C) 2021 spicyjpeg - MPL licensed + * + * This example shows how to use the psxetc DL_*() APIs to obtain information + * about the executable's symbols at runtime. This is accomplished by parsing a + * symbol map file, which is generated at compile time by GCC's nm command and + * included into the CD image. The symbol map lists all functions/variables in + * the executable and their type, address and size. Currently only searching + * for a symbol's address by its name (DL_GetSymbolByName()) is supported, + * however this may be expanded in the future. + * + * Being able to introspect local symbols at runtime, in turn, allows us to use + * the dl*() set of APIs to load, link and execute code from an external file + * (compiled with the dll.ld linker script). A dynamically-loaded library can + * reference and access any non-static function or variable within the main + * executable (and the libraries the main executable has been compiled with); + * the dynamic linker will automatically patch the DLL's code and resolve these + * references so that they point to the addresses listed in the map file. DLLs + * also have their own symbol tables, and any symbol in a DLL is accessible to + * the main executable through dlsym(). + * + * This example shows how DLLs can be loaded and unloaded at any time. Pressing + * START will unload the current DLL and load an alternate one on-the-fly. A + * custom resolver is also employed to tap into the DLL patching process and + * override the printf() function referenced by the DLLs with a different + * implementation, so the debug output from the DLLs can be redirected to the + * on-screen overlay. + * + * Dynamic linking has plenty of practical applications. It can be e.g. used to + * greatly reduce RAM usage by splitting off a large executable into a "common" + * executable (containing SDK APIs as well as frequently-used symbols such as + * rendering buffers) and many smaller DLLs, which can then be swapped in and + * out depending on which functions are needed. It can also be useful to run + * code that hasn't been compiled at the same time as the main executable, such + * as plugins/mods/patches stored on a memory card. + */ + +#include <sys/types.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <malloc.h> +#include <dlfcn.h> +#include <psxapi.h> +#include <psxetc.h> +#include <psxgte.h> +#include <psxgpu.h> +#include <psxpad.h> + +#include "library/dll_common.h" + +// List all SDK functions used by the DLLs in a dummy array to ensure GCC won't +// strip them. By default the linker removes all functions unused in the +// executable itself, but we (obviously) need them to be present for a DLL to +// call them. Placing this array in the .dummy section (as defined in the +// PSn00bSDK linker script) ensures it won't be stripped away until all +// functions are in place. +const void *const DO_NOT_STRIP[] __attribute__((section(".dummy"))) = { + &rand, + &InitGeom, + &RotMatrix, + &TransMatrix, + &MulMatrix0 +}; + +static const char *const DLL_FILENAMES[] = { + "cdrom:CUBE.DLL;1", + "cdrom:BALLS.DLL;1" +}; + +#define DLL_COUNT 2 + +void init_context(CONTEXT *ctx); +void display(CONTEXT *ctx); + +/* Symbol overriding example */ + +static volatile uint32_t resolve_counter = 0; + +// This function will override printf(), i.e. DLLs will use this instead of the +// "real" printf() present in the executable, thanks to the custom resolver +// defined below. We'll use this to redirect the DLL's output to the debug text +// window. +int dll_printf(const char *format, ...) { + va_list args; + va_start(args, format); + + char buffer[256]; + int32_t return_value = vsprintf(buffer, format, args); + va_end(args); + + FntPrint(-1, "DLL: %s", buffer); + //FntFlush(-1); + + return return_value; +} + +// This function will be called by the linker for each undefined symbol +// (function or variable) in the DLL, and should return the address of the +// symbol so the dynamic linker can patch it in. The default resolver tries to +// find them in the currently loaded symbol map using DL_GetSymbolByName(). +void *custom_resolver(DLL *dll, const char *name) { + if (!strcmp(name, "printf")) { + printf("Resolving printf() -> dll_printf() (#%d)\n", resolve_counter++); + return &dll_printf; + } + + printf("Resolving %s() (#%d)\n", name, resolve_counter++); + + // Custom resolvers should always fall back to the default behavior. + return DL_GetSymbolByName(name); +} + +/* Global variables and structs */ + +// Define a struct to store pointers to a DLL's functions into. This is not +// strictly required, however looking up symbols is a relatively slow operation +// and the pointers returned by dlsym() should be saved and reused as much as +// possible. +typedef struct { + void (*init)(CONTEXT *); + void (*render)(CONTEXT *, uint16_t buttons); +} DLL_API; + +static DLL *dll = 0; +static DLL_API dll_api; +static CONTEXT ctx; + +/* Main */ + +#define SHOW_STATUS(...) { FntPrint(-1, __VA_ARGS__); FntFlush(-1); display(&ctx); } +#define SHOW_ERROR(...) { SHOW_STATUS(__VA_ARGS__); while (1) __asm__("nop"); } + +void load_dll(const char *filename) { + if (dll) + dlclose(dll); + + SHOW_STATUS("LOADING %s\n", filename); + + dll = dlopen(filename, RTLD_LAZY); + if (!dll) + SHOW_ERROR("FAILED TO LOAD %s\n%s\n", filename, dlerror()); + + dll_api.init = dlsym(dll, "init"); + dll_api.render = dlsym(dll, "render"); + + printf("DLL init() @ %08x, render() @ %08x\n", dll_api.init, dll_api.render); + + // Unfortunately, due to how position-independent code works, function + // pointers returned by dlsym() can't be called directly. We have to use + // the DL_CALL() macro instead, which sets up register $t9 to ensure the + // function can locate and reference the DLL's relocation table. + DL_CALL(dll_api.init, &ctx); + +} + +int main(int argc, const char* argv[]) { + // As DL_LoadSymbolMap() and dlopen() rely on BIOS file APIs, the BIOS CD + // driver must be initialized by calling _InitCd() prior to loading the + // symbol map (but after setting up the GPU, for some reason). + init_context(&ctx); + + SHOW_STATUS("INITIALIZING CD\n"); + _InitCd(); + + SHOW_STATUS("LOADING SYMBOL MAP\n"); + + if (!DL_LoadSymbolMap("cdrom:MAIN.MAP;1")) + SHOW_ERROR("FAILED TO LOAD SYMBOL MAP\n%s\n", dlerror()); + + // Try to obtain a reference to a local function. + void (*_display)() = DL_GetSymbolByName("display"); + if (!_display) + SHOW_ERROR("FAILED TO LOOK UP LOCAL FUNCTION\n%s\n", dlerror()); + + printf("Symbol map test, display() @ %08x\n", _display); + + // Set up controller polling. + uint8_t pad_buff[2][34]; + InitPAD(pad_buff[0], 34, pad_buff[1], 34); + StartPAD(); + ChangeClearPAD(0); + + // Set up the custom resolver and load the first DLL. + DL_SetResolveCallback(&custom_resolver); + load_dll(DLL_FILENAMES[0]); + + uint32_t dll_active = 0; + uint16_t last_buttons = 0xffff; + + while (1) { + // Use the currently loaded DLL to render a frame. + DL_CALL(dll_api.render, &ctx, last_buttons); + + FntPrint(-1, "MAIN: DLL ADDR=%08x SIZE=%d\n", dll->ptr, dll->size); + FntPrint(-1, "MAIN: %d FUNCTIONS RESOLVED\n", resolve_counter); + FntPrint(-1, "[START] LOAD NEXT DLL\n"); + FntFlush(-1); + display(&ctx); + + // Check if a compatible controller is connected and if START has been + // pressed (i.e. wasn't previously held down, but now is). If so, + // switch the active DLL. + PADTYPE *pad = (PADTYPE *) pad_buff[0]; + if (pad->stat) + continue; + if ((pad->type != 4) && (pad->type != 5) && (pad->type != 7)) + continue; + + if ((last_buttons & PAD_START) && !(pad->btn & PAD_START)) { + dll_active++; + dll_active %= DLL_COUNT; + + load_dll(DLL_FILENAMES[dll_active]); + } + + last_buttons = pad->btn; + } + + //dlclose(dll); + //DL_UnloadSymbolMap(); + return 0; +} diff --git a/examples/system/dynlink/makefile b/examples/system/dynlink/makefile new file mode 100644 index 0000000..b3fb298 --- /dev/null +++ b/examples/system/dynlink/makefile @@ -0,0 +1,95 @@ +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions + +## Settings + +PSN00BSDK_LIBS ?= ../../../libpsn00b + +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk + +# Project target name +#TARGET = dynlink + +## Files + +# Searches for C, C++ and S (assembler) files in local directory +CFILES_MAIN = $(notdir $(wildcard *.c)) +CPPFILES_MAIN = $(notdir $(wildcard *.cpp)) +AFILES_MAIN = $(notdir $(wildcard *.s)) + +CFILES_DLL = $(notdir $(wildcard library/*.c)) +CPPFILES_DLL = $(notdir $(wildcard library/*.cpp)) +AFILES_DLL = $(notdir $(wildcard library/*.s)) + +# Create names for object files +OFILES_MAIN = $(addprefix build/,$(CFILES_MAIN:.c=.o)) \ + $(addprefix build/,$(CPPFILES_MAIN:.cpp=.o)) \ + $(addprefix build/,$(AFILES_MAIN:.s=.o)) +OFILES_CUBE = build/library/cube.o +OFILES_BALLS = build/library/balls.o + +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += + +## Build rules + +all: iso +#all: build/library/cube build/library/balls build/main + +iso: build/library/cube build/library/balls build/main resources + $(MKPSXISO) -y -q iso.xml + +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml + +build/library/cube: $(OFILES_CUBE) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_DLL) $^ -o $@ + #$(NM) -f posix -l -n $@ >$@.map + $(OBJCOPY) -O binary $@ $@.dll + +build/library/balls: $(OFILES_BALLS) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_DLL) $^ -o $@ + #$(NM) -f posix -l -n $@ >$@.map + $(OBJCOPY) -O binary $@ $@.dll + +build/main: $(OFILES_MAIN) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXEDYN) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe + +build/library/%.o: library/%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS_DLL) $(INCLUDE) -c $< -o $@ + +build/library/%.o: library/%.cpp + @mkdir -p $(dir $@) + $(CXX) $(CPPFLAGS_DLL) $(INCLUDE) -c $< -o $@ + +build/library/%.o: library/%.s + @mkdir -p $(dir $@) + $(CC) $(AFLAGS_DLL) $(INCLUDE) -c $< -o $@ + +build/%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS_EXEDYN) $(INCLUDE) -c $< -o $@ + +build/%.o: %.cpp + @mkdir -p $(dir $@) + $(CXX) $(CPPFLAGS_EXEDYN) $(INCLUDE) -c $< -o $@ + +build/%.o: %.s + @mkdir -p $(dir $@) + $(CC) $(AFLAGS_EXEDYN) $(INCLUDE) -c $< -o $@ + +clean: + rm -rf build diff --git a/examples/system/dynlink/system.cnf b/examples/system/dynlink/system.cnf new file mode 100644 index 0000000..a4a2146 --- /dev/null +++ b/examples/system/dynlink/system.cnf @@ -0,0 +1,4 @@ +BOOT=cdrom:\main.exe;1 +TCB=4 +EVENT=10 +STACK=801FFFF0 |
