blues/sys_psp.c

429 lines
11 KiB
C
Executable File

#include <pspaudio.h>
#include <pspaudiolib.h>
#include <pspctrl.h>
#include <pspdisplay.h>
#include <pspgu.h>
#include <pspkernel.h>
#include <pspsdk.h>
#include <malloc.h>
#include "sys.h"
PSP_MODULE_INFO("Blues Brothers", 0, 1, 0);
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER);
static uint32_t _buttons;
static sys_audio_cb _audio_proc;
static void *_audio_param;
static int _audio_channel;
static SceUID _audio_mutex;
static uint32_t _fb_offset;
// static const int AUDIO_FREQ = 44100;
static const int AUDIO_SAMPLES_COUNT = 2048;
static const int SCREEN_W = 480;
static const int SCREEN_H = 272;
static const int SCREEN_PITCH = 512;
static uint32_t __attribute__((aligned(16))) _dlist[262144];
static uint32_t __attribute__((aligned(16))) _clut[256];
#define MAX_SPRITES 256
struct vertex_t {
int16_t u, v;
int16_t x, y, z;
};
struct spritetexture_t {
int w, h;
uint8_t *bitmap;
int count;
struct sys_rect_t *r;
};
static struct spritetexture_t _spritetextures[RENDER_SPR_COUNT];
struct sprite_t {
struct vertex_t v[2];
int tex;
};
static struct sprite_t _sprites[MAX_SPRITES];
static int _sprites_count;
static void print_log(FILE *fp, const char *s) {
static bool first_open = false;
if (!first_open) {
fp = fopen("stdout.txt", "w");
first_open = true;
} else {
fp = fopen("stdout.txt", "a");
}
fprintf(fp, "%s\n", s);
fclose(fp);
}
static int exit_callback(int arg1, int arg2, void *common) {
g_sys.input.quit = true;
return 0;
}
static int callback_thread(SceSize args, void *argp) {
const int cb = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
sceKernelRegisterExitCallback(cb);
sceKernelSleepThreadCB();
return 0;
}
static void psp_init() {
_buttons = 0;
memset(&_audio_proc, 0, sizeof(_audio_proc));
_audio_channel = -1;
_audio_mutex = 0;
const int th = sceKernelCreateThread("update_thread", callback_thread, 0x11, 0xFA0, 0, 0);
if (th >= 0) {
sceKernelStartThread(th, 0, 0);
}
sceKernelDcacheWritebackAll();
sceCtrlSetSamplingCycle(0);
sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
}
static void psp_fini() {
sceGuTerm();
sceKernelExitGame();
}
static void psp_set_screen_size(int w, int h, const char *caption, int scale, const char *filter, bool fullscreen) {
sceGuInit();
sceGuStart(GU_DIRECT, _dlist);
const int fbSize = SCREEN_PITCH * SCREEN_H * sizeof(uint32_t); // rgba
uint32_t vramOffset = 0;
sceGuDrawBuffer(GU_PSM_8888, (void *)vramOffset, SCREEN_PITCH); vramOffset += fbSize;
sceGuDispBuffer(SCREEN_W, SCREEN_H, (void *)vramOffset, SCREEN_PITCH); vramOffset += fbSize;
sceGuOffset(2048 - (SCREEN_W / 2), 2048 - (SCREEN_H / 2));
sceGuViewport(2048, 2048, SCREEN_W, SCREEN_H);
sceGuScissor(0, 0, SCREEN_W, SCREEN_H);
sceGuEnable(GU_SCISSOR_TEST);
sceGuEnable(GU_TEXTURE_2D);
sceGuDisable(GU_DEPTH_TEST);
sceGuClearColor(0);
sceGuClear(GU_COLOR_BUFFER_BIT);
sceGuFinish();
sceGuSync(GU_SYNC_WHAT_DONE, GU_SYNC_FINISH);
sceDisplayWaitVblankStart();
_fb_offset = (uint32_t)sceGuSwapBuffers();
sceGuDisplay(GU_TRUE);
for (int i = 0; i < 256; ++i) {
_clut[i] = GU_RGBA(0, 0, 0, 255);
}
sceKernelDcacheWritebackRange(_clut, sizeof(_clut));
}
static void psp_set_screen_palette(const uint8_t *colors, int offset, int count, int depth) {
const int shift = 8 - depth;
for (int i = 0; i < count; ++i) {
int r = *colors++;
int g = *colors++;
int b = *colors++;
if (shift != 0) {
r = (r << shift) | (r >> (depth - shift));
g = (g << shift) | (g >> (depth - shift));
b = (b << shift) | (b >> (depth - shift));
}
_clut[offset + i] = GU_RGBA(r, g, b, 255);
}
sceKernelDcacheWritebackRange(_clut, sizeof(_clut));
}
static uint32_t convert_amiga_color(uint16_t color) {
uint8_t r = (color >> 8) & 15;
r |= r << 4;
uint8_t g = (color >> 4) & 15;
g |= g << 4;
uint8_t b = color & 15;
b |= b << 4;
return GU_RGBA(r, g, b, 255);
}
static void psp_set_palette_amiga(const uint16_t *colors, int offset) {
for (int i = 0; i < 16; ++i) {
_clut[offset + i] = convert_amiga_color(colors[i]);
}
sceKernelDcacheWritebackRange(_clut, sizeof(_clut));
}
static void psp_set_copper_bars(const uint16_t *data) {
}
static void psp_set_palette_color(int i, const uint8_t *colors) {
_clut[i] = GU_RGBA(colors[0], colors[1], colors[2], 255);
sceKernelDcacheWritebackRange(_clut, sizeof(_clut));
}
static void psp_fade_in_palette() {
sceDisplayWaitVblankStart();
_fb_offset = (uint32_t)sceGuSwapBuffers();
}
static void psp_fade_out_palette() {
uint32_t *buffer = (uint32_t *)(((uint8_t *)sceGeEdramGetAddr()) + _fb_offset);
for (int y = 0; y < SCREEN_H; ++y) {
memset(buffer, 0, SCREEN_W * sizeof(uint32_t));
buffer += SCREEN_PITCH;
}
}
static void psp_transition_screen(enum sys_transition_e type, bool open) {
}
static void psp_copy_bitmap(const uint8_t *p, int w, int h) {
const int dx = (SCREEN_W - w) / 2;
const int dy = (SCREEN_H - h) / 2;
uint32_t *buffer = (uint32_t *)(((uint8_t *)sceGeEdramGetAddr()) + _fb_offset);
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
buffer[(dy + y) * SCREEN_PITCH + (dx + x)] = _clut[*p++];
}
}
}
static void psp_update_screen() {
sceGuStart(GU_DIRECT, _dlist);
sceGuClutMode(GU_PSM_8888, 0, 0xFF, 0);
sceGuClutLoad(256 / 8, _clut);
sceGuTexMode(GU_PSM_T8, 0, 0, 0);
sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGB);
sceGuTexFilter(GU_NEAREST, GU_NEAREST);
sceGuEnable(GU_COLOR_TEST);
sceGuColorFunc(GU_NOTEQUAL, _clut[0], 0xFFFFFF);
int prev_tex = -1;
for (int i = 0; i < _sprites_count; ++i) {
struct sprite_t *spr = &_sprites[i];
if (prev_tex != spr->tex) {
struct spritetexture_t *st = &_spritetextures[spr->tex];
sceGuTexImage(0, st->w, st->h, st->w, st->bitmap);
prev_tex = spr->tex;
}
struct vertex_t *v = (struct vertex_t *)sceGuGetMemory(2 * sizeof(struct vertex_t));
v[0] = spr->v[0];
v[1] = spr->v[1];
sceGuDrawArray(GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, v);
}
sceGuDisable(GU_COLOR_TEST);
sceGuFinish();
sceGuSync(GU_SYNC_WHAT_DONE, GU_SYNC_FINISH);
sceDisplayWaitVblankStart();
_fb_offset = (uint32_t)sceGuSwapBuffers();
}
static void psp_shake_screen(int dx, int dy) {
sceGuOffset(2048 - (SCREEN_W / 2) + dx, 2048 - (SCREEN_H / 2) + dy);
}
static void psp_process_events() {
g_sys.input.direction = 0;
static const struct {
int psp;
int sys;
} mapping[] = {
{ PSP_CTRL_UP, INPUT_DIRECTION_UP },
{ PSP_CTRL_RIGHT, INPUT_DIRECTION_RIGHT },
{ PSP_CTRL_DOWN, INPUT_DIRECTION_DOWN },
{ PSP_CTRL_LEFT, INPUT_DIRECTION_LEFT },
{ 0, 0 }
};
SceCtrlData data;
sceCtrlPeekBufferPositive(&data, 1);
for (int i = 0; mapping[i].psp != 0; ++i) {
if (data.Buttons & mapping[i].psp) {
g_sys.input.direction |= mapping[i].sys;
}
}
static const int lxMargin = 64;
if (data.Lx < 127 - lxMargin) {
g_sys.input.direction |= INPUT_DIRECTION_LEFT;
} else if (data.Lx > 127 + lxMargin) {
g_sys.input.direction |= INPUT_DIRECTION_RIGHT;
}
static const int lyMargin = 64;
if (data.Ly < 127 - lyMargin) {
g_sys.input.direction |= INPUT_DIRECTION_UP;
} else if (data.Ly > 127 + lyMargin) {
g_sys.input.direction |= INPUT_DIRECTION_DOWN;
}
// PSP_CTRL_CROSS
g_sys.input.space = (data.Buttons & PSP_CTRL_SQUARE) != 0;
g_sys.input.jump = (data.Buttons & PSP_CTRL_CIRCLE) != 0;
// PSP_CTRL_TRIANGLE
const uint32_t mask = data.Buttons ^ _buttons;
if ((data.Buttons & PSP_CTRL_LTRIGGER) & mask) {
}
if ((data.Buttons & PSP_CTRL_RTRIGGER) & mask) {
}
_buttons = data.Buttons;
}
static void psp_sleep(int duration) {
sceKernelDelayThread(duration * 1000);
}
static uint32_t psp_get_timestamp() {
struct timeval tv;
sceKernelLibcGettimeofday(&tv, 0);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
static void psp_lock_audio() {
sceKernelWaitSema(_audio_mutex, 1, 0);
}
static void psp_unlock_audio() {
sceKernelSignalSema(_audio_mutex, 1);
}
static void audio_callback(void *buf, unsigned int samples, void *param) { // 44100hz S16 stereo
int16_t buf22khz[samples];
memset(buf22khz, 0, sizeof(buf22khz));
psp_lock_audio();
_audio_proc(_audio_param, (uint8_t *)buf22khz, samples);
psp_unlock_audio();
uint32_t *buf44khz = (uint32_t *)buf;
static int16_t prev;
for (unsigned int i = 0; i < samples; ++i) {
const int16_t current = buf22khz[i];
buf44khz[i] = (current << 16) | (((prev + current) >> 1) & 0xFFFF);
prev = current;
}
}
static void psp_start_audio(sys_audio_cb callback, void *param) {
// sceAudioSetFrequency(AUDIO_FREQ);
pspAudioInit();
_audio_proc = callback;
_audio_param = param;
_audio_mutex = sceKernelCreateSema("audio_lock", 0, 1, 1, 0);
_audio_channel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, AUDIO_SAMPLES_COUNT, PSP_AUDIO_FORMAT_STEREO);
pspAudioSetChannelCallback(_audio_channel, audio_callback, 0);
}
static void psp_stop_audio() {
sceAudioChRelease(_audio_channel);
sceKernelDeleteSema(_audio_mutex);
pspAudioEnd();
}
static void render_load_sprites(int spr_type, int count, const struct sys_rect_t *r, const uint8_t *data, int w, int h, uint8_t color_key, bool update_pal) {
struct spritetexture_t *st = &_spritetextures[spr_type];
st->r = (struct sys_rect_t *)malloc(count * sizeof(struct sys_rect_t));
if (st->r) {
memcpy(st->r, r, count * sizeof(struct sys_rect_t));
st->count = count;
}
st->bitmap = (uint8_t *)memalign(16, w * h);
if (st->bitmap) {
memcpy(st->bitmap, data, w * h);
st->w = w;
st->h = h;
sceKernelDcacheWritebackRange(st->bitmap, w * h);
}
}
static void render_unload_sprites(int spr_type) {
struct spritetexture_t *st = &_spritetextures[spr_type];
if (st->r) {
free(st->r);
}
if (st->bitmap) {
free(st->bitmap);
}
memset(st, 0, sizeof(struct spritetexture_t));
}
static void render_add_sprite(int spr_type, int frame, int x, int y, int xflip) {
if (_sprites_count < MAX_SPRITES) {
struct sprite_t *spr = &_sprites[_sprites_count];
struct vertex_t *v = spr->v;
struct sys_rect_t *r = &_spritetextures[spr_type].r[frame];
if (!xflip) {
v[0].u = r->x; v[1].u = r->x + r->w;
} else {
v[0].u = r->x + r->w; v[1].u = r->x;
}
v[0].v = r->y; v[1].v = r->y + r->h;
v[0].x = x; v[1].x = x + r->w;
v[0].y = y; v[1].y = y + r->h;
v[0].z = 0; v[1].z = 0;
spr->tex = spr_type;
++_sprites_count;
}
}
static void render_clear_sprites() {
_sprites_count = 0;
}
static void render_set_sprites_clipping_rect(int x, int y, int w, int h) {
sceGuScissor(x, y, w, h);
}
struct sys_t g_sys = {
.init = psp_init,
.fini = psp_fini,
.set_screen_size = psp_set_screen_size,
.set_screen_palette = psp_set_screen_palette,
.set_palette_amiga = psp_set_palette_amiga,
.set_copper_bars = psp_set_copper_bars,
.set_palette_color = psp_set_palette_color,
.fade_in_palette = psp_fade_in_palette,
.fade_out_palette = psp_fade_out_palette,
.copy_bitmap = psp_copy_bitmap,
.update_screen = psp_update_screen,
.shake_screen = psp_shake_screen,
.transition_screen = psp_transition_screen,
.process_events = psp_process_events,
.sleep = psp_sleep,
.get_timestamp = psp_get_timestamp,
.start_audio = psp_start_audio,
.stop_audio = psp_stop_audio,
.lock_audio = psp_lock_audio,
.unlock_audio = psp_unlock_audio,
.render_load_sprites = render_load_sprites,
.render_unload_sprites = render_unload_sprites,
.render_add_sprite = render_add_sprite,
.render_clear_sprites = render_clear_sprites,
.render_set_sprites_clipping_rect = render_set_sprites_clipping_rect,
.print_log = print_log,
};