messy experimentation commit
This commit is contained in:
parent
ec15d449e1
commit
f6f8fa6a8f
13 changed files with 2472 additions and 426 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
.DS_Store
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
*.elf
|
||||||
415
src/common/api.c
Normal file
415
src/common/api.c
Normal file
|
|
@ -0,0 +1,415 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <linux/fb.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <SDL/SDL.h>
|
||||||
|
#include <SDL/SDL_image.h>
|
||||||
|
#include <SDL/SDL_ttf.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "api.h"
|
||||||
|
#include "defines.h"
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
// TODO: tmp
|
||||||
|
void powerOff(void) {
|
||||||
|
system("echo u > /proc/sysrq-trigger");
|
||||||
|
system("echo s > /proc/sysrq-trigger");
|
||||||
|
system("echo o > /proc/sysrq-trigger");
|
||||||
|
}
|
||||||
|
void fauxSleep(void) { }
|
||||||
|
int preventAutosleep(void) { return 0; }
|
||||||
|
int isCharging() { return 0; }
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
void LOG_note(int level, const char* fmt, ...) {
|
||||||
|
char buf[1024] = {0};
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
switch(level) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
case LOG_DEBUG:
|
||||||
|
printf("DEBUG: %s", buf);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case LOG_INFO:
|
||||||
|
printf("INFO: %s", buf);
|
||||||
|
break;
|
||||||
|
case LOG_WARN:
|
||||||
|
fprintf(stderr, "WARN: %s", buf);
|
||||||
|
break;
|
||||||
|
case LOG_ERROR:
|
||||||
|
fprintf(stderr, "ERROR: %s", buf);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
static struct GFX_Context {
|
||||||
|
int fb;
|
||||||
|
int pitch;
|
||||||
|
int buffer;
|
||||||
|
int buffer_size;
|
||||||
|
int map_size;
|
||||||
|
void* map;
|
||||||
|
struct fb_var_screeninfo vinfo;
|
||||||
|
struct fb_fix_screeninfo finfo;
|
||||||
|
|
||||||
|
SDL_Surface* screen;
|
||||||
|
} gfx;
|
||||||
|
SDL_Surface* GFX_init(void) {
|
||||||
|
SDL_Init(SDL_INIT_VIDEO);
|
||||||
|
SDL_ShowCursor(0);
|
||||||
|
TTF_Init();
|
||||||
|
|
||||||
|
// char namebuf[MAX_PATH];
|
||||||
|
// if (SDL_VideoDriverName(namebuf, MAX_PATH)) {
|
||||||
|
// printf("SDL_VideoDriverName: %s\n", namebuf);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// SDL_Rect **modes = SDL_ListModes(NULL, SDL_HWSURFACE);
|
||||||
|
// if(modes == (SDL_Rect **)0){
|
||||||
|
// puts("No modes available!");
|
||||||
|
// exit(-1);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if(modes == (SDL_Rect **)-1){
|
||||||
|
// puts("All resolutions available.");
|
||||||
|
// }
|
||||||
|
// else{
|
||||||
|
// puts("Available Modes");
|
||||||
|
// for(int i=0; modes[i]; ++i) {
|
||||||
|
// printf("\t%d x %d\n", modes[i]->w, modes[i]->h);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// we're drawing to the (triple-buffered) framebuffer directly
|
||||||
|
// but we still need to set video mode to initialize input events
|
||||||
|
SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH, SDL_HWSURFACE);
|
||||||
|
|
||||||
|
// open framebuffer
|
||||||
|
gfx.fb = open("/dev/fb0", O_RDWR);
|
||||||
|
|
||||||
|
// configure framebuffer
|
||||||
|
ioctl(gfx.fb, FBIOGET_VSCREENINFO, &gfx.vinfo);
|
||||||
|
gfx.vinfo.bits_per_pixel = SCREEN_DEPTH;
|
||||||
|
gfx.vinfo.xres = SCREEN_WIDTH;
|
||||||
|
gfx.vinfo.yres = SCREEN_HEIGHT;
|
||||||
|
gfx.vinfo.xres_virtual = SCREEN_WIDTH;
|
||||||
|
gfx.vinfo.yres_virtual = SCREEN_HEIGHT * SCREEN_BUFFER_COUNT;
|
||||||
|
gfx.vinfo.xoffset = 0;
|
||||||
|
gfx.vinfo.activate = FB_ACTIVATE_VBL;
|
||||||
|
|
||||||
|
// gfx.vinfo.pixclock = 0xb7bb;// 0xc350; // 0xc350=56 0xb7e0=59.94 0xb7bb=60
|
||||||
|
// gfx.vinfo.left_margin = 0x10;
|
||||||
|
// gfx.vinfo.right_margin = 0x14;
|
||||||
|
// gfx.vinfo.upper_margin = 0x0f;
|
||||||
|
// gfx.vinfo.lower_margin = 0x05;
|
||||||
|
// gfx.vinfo.hsync_len = 0x1e;
|
||||||
|
// gfx.vinfo.vsync_len = 0x02;
|
||||||
|
|
||||||
|
ioctl(gfx.fb, FBIOPUT_VSCREENINFO, &gfx.vinfo);
|
||||||
|
|
||||||
|
// printf("pixclock: 0x%04x\n", gfx.vinfo.pixclock);
|
||||||
|
// printf("left_margin: 0x%02x\n", gfx.vinfo.left_margin);
|
||||||
|
// printf("right_margin: 0x%02x\n", gfx.vinfo.right_margin);
|
||||||
|
// printf("upper_margin: 0x%02x\n", gfx.vinfo.upper_margin);
|
||||||
|
// printf("lower_margin: 0x%02x\n", gfx.vinfo.lower_margin);
|
||||||
|
// printf("hsync_len: 0x%02x\n", gfx.vinfo.hsync_len);
|
||||||
|
// printf("vsync_len: 0x%02x\n", gfx.vinfo.vsync_len);
|
||||||
|
|
||||||
|
// get fixed screen info
|
||||||
|
ioctl(gfx.fb, FBIOGET_FSCREENINFO, &gfx.finfo);
|
||||||
|
gfx.map_size = gfx.finfo.smem_len;
|
||||||
|
gfx.map = mmap(0, gfx.map_size, PROT_READ | PROT_WRITE, MAP_SHARED, gfx.fb, 0);
|
||||||
|
|
||||||
|
// struct fb_vblank vblank;
|
||||||
|
// ioctl(gfx.fb, FBIOGET_VBLANK, &vblank);
|
||||||
|
// printf("flags: %i\n", vblank.flags);
|
||||||
|
// printf("count: %i\n", vblank.count);
|
||||||
|
// printf("vcount: %i\n", vblank.vcount);
|
||||||
|
// printf("hcount: %i\n", vblank.hcount);
|
||||||
|
|
||||||
|
// buffer tracking
|
||||||
|
gfx.buffer = 0;
|
||||||
|
gfx.buffer_size = SCREEN_PITCH * SCREEN_HEIGHT;
|
||||||
|
|
||||||
|
// return screen
|
||||||
|
gfx.screen = SDL_CreateRGBSurfaceFrom(gfx.map, SCREEN_WIDTH,SCREEN_HEIGHT, SCREEN_DEPTH,SCREEN_PITCH, 0,0,0,0);
|
||||||
|
return gfx.screen;
|
||||||
|
}
|
||||||
|
void GFX_clear(SDL_Surface* screen) {
|
||||||
|
memset(screen->pixels, 0, gfx.buffer_size);
|
||||||
|
}
|
||||||
|
void GFX_clearAll(void) {
|
||||||
|
memset(gfx.map, 0, gfx.map_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #define OWL_IOW(num, dtype) _IOW('O', num, dtype)
|
||||||
|
// #define OWLFB_WAITFORVSYNC OWL_IOW(57,long long)
|
||||||
|
|
||||||
|
void GFX_flip(SDL_Surface* screen) {
|
||||||
|
// struct fb_vblank vblank;
|
||||||
|
// ioctl(gfx.fb, FBIOGET_VBLANK, &vblank);
|
||||||
|
// printf("flags: %i\n", vblank.flags);
|
||||||
|
// printf("count: %i\n", vblank.count);
|
||||||
|
// printf("vcount: %i\n", vblank.vcount);
|
||||||
|
// printf("hcount: %i\n", vblank.hcount);
|
||||||
|
|
||||||
|
// TODO: this would be moved to a thread
|
||||||
|
// I'm not clear on why that would be necessary
|
||||||
|
// if it's non-blocking and the pan will wait
|
||||||
|
// until the next vblank...
|
||||||
|
// what if the scaling was also moved to a thread?
|
||||||
|
gfx.vinfo.yoffset = gfx.buffer * SCREEN_HEIGHT;
|
||||||
|
int arg = 0;
|
||||||
|
// ioctl(gfx.fb, OWLFB_WAITFORVSYNC, &arg); // TODO: this doesn't wait but it also doesn't error out like FBIO_WAITFORVSYNC...
|
||||||
|
ioctl(gfx.fb, FBIOPAN_DISPLAY, &gfx.vinfo);
|
||||||
|
|
||||||
|
gfx.buffer += 1;
|
||||||
|
if (gfx.buffer>=SCREEN_BUFFER_COUNT) gfx.buffer -= SCREEN_BUFFER_COUNT;
|
||||||
|
screen->pixels = gfx.map + (gfx.buffer * gfx.buffer_size);
|
||||||
|
}
|
||||||
|
void GFX_quit(void) {
|
||||||
|
GFX_clearAll();
|
||||||
|
munmap(gfx.map, gfx.map_size);
|
||||||
|
close(gfx.fb);
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
// based on picoarch's audio
|
||||||
|
// implementation, rewritten
|
||||||
|
// to understand it better
|
||||||
|
|
||||||
|
#define MAX_SAMPLE_RATE 48000
|
||||||
|
#define BATCH_SIZE 100
|
||||||
|
|
||||||
|
typedef int (*SND_Resampler)(const SND_Frame frame);
|
||||||
|
static struct SND_Context {
|
||||||
|
double frame_rate;
|
||||||
|
|
||||||
|
int sample_rate_in;
|
||||||
|
int sample_rate_out;
|
||||||
|
|
||||||
|
int buffer_seconds; // current_audio_buffer_size
|
||||||
|
SND_Frame* buffer; // buf
|
||||||
|
size_t frame_count; // buf_len
|
||||||
|
|
||||||
|
int frame_in; // buf_w
|
||||||
|
int frame_out; // buf_r
|
||||||
|
int frame_filled; // max_buf_w
|
||||||
|
|
||||||
|
SND_Resampler resample;
|
||||||
|
} snd;
|
||||||
|
static void SND_audioCallback(void* userdata, uint8_t* stream, int len) { // plat_sound_callback
|
||||||
|
if (snd.frame_count==0) return;
|
||||||
|
|
||||||
|
int16_t *out = (int16_t *)stream;
|
||||||
|
len /= (sizeof(int16_t) * 2);
|
||||||
|
|
||||||
|
while (snd.frame_out!=snd.frame_in && len>0) {
|
||||||
|
*out++ = snd.buffer[snd.frame_out].left;
|
||||||
|
*out++ = snd.buffer[snd.frame_out].right;
|
||||||
|
|
||||||
|
snd.frame_filled = snd.frame_out;
|
||||||
|
|
||||||
|
snd.frame_out += 1;
|
||||||
|
len -= 1;
|
||||||
|
|
||||||
|
if (snd.frame_out>=snd.frame_count) snd.frame_out = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (len>0) {
|
||||||
|
*out++ = 0;
|
||||||
|
*out++ = 0;
|
||||||
|
len -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void SND_resizeBuffer(void) { // plat_sound_resize_buffer
|
||||||
|
snd.frame_count = snd.buffer_seconds * snd.sample_rate_in / snd.frame_rate;
|
||||||
|
if (snd.frame_count==0) return;
|
||||||
|
|
||||||
|
SDL_LockAudio();
|
||||||
|
|
||||||
|
int buffer_bytes = snd.frame_count * sizeof(SND_Frame);
|
||||||
|
snd.buffer = realloc(snd.buffer, buffer_bytes);
|
||||||
|
|
||||||
|
memset(snd.buffer, 0, buffer_bytes);
|
||||||
|
|
||||||
|
snd.frame_in = 0;
|
||||||
|
snd.frame_out = 0;
|
||||||
|
snd.frame_filled = snd.frame_count - 1;
|
||||||
|
|
||||||
|
SDL_UnlockAudio();
|
||||||
|
}
|
||||||
|
static int SND_resampleNone(SND_Frame frame) { // audio_resample_passthrough
|
||||||
|
snd.buffer[snd.frame_in++] = frame;
|
||||||
|
if (snd.frame_in >= snd.frame_count) snd.frame_in = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
static int SND_resampleNear(SND_Frame frame) { // audio_resample_nearest
|
||||||
|
static int diff = 0;
|
||||||
|
int consumed = 0;
|
||||||
|
|
||||||
|
if (diff < snd.sample_rate_out) {
|
||||||
|
snd.buffer[snd.frame_in++] = frame;
|
||||||
|
if (snd.frame_in >= snd.frame_count) snd.frame_in = 0;
|
||||||
|
diff += snd.sample_rate_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diff >= snd.sample_rate_out) {
|
||||||
|
consumed++;
|
||||||
|
diff -= snd.sample_rate_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
static void SND_selectResampler(void) { // plat_sound_select_resampler
|
||||||
|
if (snd.sample_rate_in==snd.sample_rate_out) {
|
||||||
|
snd.resample = SND_resampleNone;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
snd.resample = SND_resampleNear;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size_t SND_batchSamples(const SND_Frame* frames, size_t frame_count) { // plat_sound_write / plat_sound_write_resample
|
||||||
|
if (snd.frame_count==0) return 0;
|
||||||
|
|
||||||
|
SDL_LockAudio();
|
||||||
|
|
||||||
|
int consumed = 0;
|
||||||
|
while (frame_count > 0) {
|
||||||
|
int tries = 0;
|
||||||
|
int amount = MIN(BATCH_SIZE, frame_count);
|
||||||
|
|
||||||
|
while (tries < 10 && snd.frame_in==snd.frame_filled) {
|
||||||
|
tries++;
|
||||||
|
SDL_UnlockAudio();
|
||||||
|
SDL_Delay(1);
|
||||||
|
SDL_LockAudio();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (amount && snd.frame_in != snd.frame_filled) {
|
||||||
|
consumed = snd.resample(*frames);
|
||||||
|
frames += consumed;
|
||||||
|
amount -= consumed;
|
||||||
|
frame_count -= consumed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_UnlockAudio();
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SND_init(double sample_rate, double frame_rate) { // plat_sound_init
|
||||||
|
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||||
|
|
||||||
|
snd.frame_rate = frame_rate;
|
||||||
|
|
||||||
|
SDL_AudioSpec spec_in;
|
||||||
|
SDL_AudioSpec spec_out;
|
||||||
|
|
||||||
|
spec_in.freq = MIN(sample_rate, MAX_SAMPLE_RATE); // TODO: always MAX_SAMPLE_RATE on Miyoo Mini? use #ifdef PLATFORM_MIYOOMINI?
|
||||||
|
spec_in.format = AUDIO_S16;
|
||||||
|
spec_in.channels = 2;
|
||||||
|
spec_in.samples = 512;
|
||||||
|
spec_in.callback = SND_audioCallback;
|
||||||
|
|
||||||
|
SDL_OpenAudio(&spec_in, &spec_out);
|
||||||
|
|
||||||
|
snd.buffer_seconds = 5;
|
||||||
|
snd.sample_rate_in = sample_rate;
|
||||||
|
snd.sample_rate_out = spec_out.freq;
|
||||||
|
|
||||||
|
SND_selectResampler();
|
||||||
|
SND_resizeBuffer();
|
||||||
|
|
||||||
|
SDL_PauseAudio(0);
|
||||||
|
}
|
||||||
|
void SND_quit(void) { // plat_sound_finish
|
||||||
|
SDL_PauseAudio(1);
|
||||||
|
SDL_CloseAudio();
|
||||||
|
|
||||||
|
if (snd.buffer) {
|
||||||
|
free(snd.buffer);
|
||||||
|
snd.buffer = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
static struct PAD_Context {
|
||||||
|
int is_pressed;
|
||||||
|
int just_pressed;
|
||||||
|
int just_released;
|
||||||
|
} pad;
|
||||||
|
void PAD_reset(void) {
|
||||||
|
pad.just_pressed = BTN_NONE;
|
||||||
|
pad.is_pressed = BTN_NONE;
|
||||||
|
pad.just_released = BTN_NONE;
|
||||||
|
}
|
||||||
|
void PAD_poll(void) {
|
||||||
|
// reset transient state
|
||||||
|
pad.just_pressed = BTN_NONE;
|
||||||
|
pad.just_released = BTN_NONE;
|
||||||
|
|
||||||
|
// the actual poll
|
||||||
|
SDL_Event event;
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
int btn = BTN_NONE;
|
||||||
|
if (event.type==SDL_KEYDOWN || event.type==SDL_KEYUP) {
|
||||||
|
uint8_t code = event.key.keysym.scancode;
|
||||||
|
if (code==CODE_UP) btn = BTN_UP;
|
||||||
|
else if (code==CODE_DOWN) btn = BTN_DOWN;
|
||||||
|
else if (code==CODE_LEFT) btn = BTN_LEFT;
|
||||||
|
else if (code==CODE_RIGHT) btn = BTN_RIGHT;
|
||||||
|
else if (code==CODE_A) btn = BTN_A;
|
||||||
|
else if (code==CODE_B) btn = BTN_B;
|
||||||
|
else if (code==CODE_X) btn = BTN_X;
|
||||||
|
else if (code==CODE_Y) btn = BTN_Y;
|
||||||
|
else if (code==CODE_START) btn = BTN_START;
|
||||||
|
else if (code==CODE_SELECT) btn = BTN_SELECT;
|
||||||
|
else if (code==CODE_MENU) btn = BTN_MENU;
|
||||||
|
else if (code==CODE_L1) btn = BTN_L1;
|
||||||
|
else if (code==CODE_L2) btn = BTN_L2;
|
||||||
|
else if (code==CODE_R1) btn = BTN_R1;
|
||||||
|
else if (code==CODE_R2) btn = BTN_R2;
|
||||||
|
else if (code==CODE_VOL_UP) btn = BTN_VOL_UP;
|
||||||
|
else if (code==CODE_VOL_DN) btn = BTN_VOL_DN;
|
||||||
|
else if (code==CODE_POWER) btn = BTN_POWER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btn==BTN_NONE) continue;
|
||||||
|
|
||||||
|
if (event.type==SDL_KEYUP) {
|
||||||
|
pad.is_pressed &= ~btn; // unset
|
||||||
|
pad.just_released |= btn; // set
|
||||||
|
}
|
||||||
|
else if ((pad.is_pressed & btn)==BTN_NONE) {
|
||||||
|
pad.just_pressed |= btn; // set
|
||||||
|
pad.is_pressed |= btn; // set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: switch to macros? not if I want to move it to a separate file
|
||||||
|
int PAD_anyPressed(void) { return pad.is_pressed!=BTN_NONE; }
|
||||||
|
int PAD_justPressed(int btn) { return pad.just_pressed & btn; }
|
||||||
|
int PAD_isPressed(int btn) { return pad.is_pressed & btn; }
|
||||||
|
int PAD_justReleased(int btn) { return pad.just_released & btn; }
|
||||||
85
src/common/api.h
Normal file
85
src/common/api.h
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
#ifndef __API_H__
|
||||||
|
#define __API_H__
|
||||||
|
#include <SDL/SDL.h>
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
// TODO: tmp
|
||||||
|
void powerOff(void);
|
||||||
|
void fauxSleep(void);
|
||||||
|
int preventAutosleep(void);
|
||||||
|
int isCharging();
|
||||||
|
#define PAD_justRepeated PAD_justPressed
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LOG_DEBUG = 0,
|
||||||
|
LOG_INFO,
|
||||||
|
LOG_WARN,
|
||||||
|
LOG_ERROR,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define LOG_debug(fmt, ...) LOG_note(LOG_DEBUG, fmt, ##__VA_ARGS__)
|
||||||
|
#define LOG_info(fmt, ...) LOG_note(LOG_INFO, fmt, ##__VA_ARGS__)
|
||||||
|
#define LOG_warn(fmt, ...) LOG_note(LOG_WARN, fmt, ##__VA_ARGS__)
|
||||||
|
#define LOG_error(fmt, ...) LOG_note(LOG_ERROR, fmt, ##__VA_ARGS__)
|
||||||
|
void LOG_note(int level, const char* fmt, ...);
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
SDL_Surface* GFX_init(void);
|
||||||
|
void GFX_clear(SDL_Surface* screen);
|
||||||
|
void GFX_clearAll(void);
|
||||||
|
void GFX_flip(SDL_Surface* screen);
|
||||||
|
void GFX_quit(void);
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
typedef struct SND_Frame {
|
||||||
|
int16_t left;
|
||||||
|
int16_t right;
|
||||||
|
} SND_Frame;
|
||||||
|
|
||||||
|
void SND_init(double sample_rate, double frame_rate);
|
||||||
|
size_t SND_batchSamples(const SND_Frame* frames, size_t frame_count);
|
||||||
|
void SND_quit(void);
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
enum {
|
||||||
|
BTN_NONE = 0,
|
||||||
|
BTN_UP = 1 << 0,
|
||||||
|
BTN_DOWN = 1 << 1,
|
||||||
|
BTN_LEFT = 1 << 2,
|
||||||
|
BTN_RIGHT = 1 << 3,
|
||||||
|
BTN_A = 1 << 4,
|
||||||
|
BTN_B = 1 << 5,
|
||||||
|
BTN_X = 1 << 6,
|
||||||
|
BTN_Y = 1 << 7,
|
||||||
|
BTN_START = 1 << 8,
|
||||||
|
BTN_SELECT = 1 << 9,
|
||||||
|
BTN_L1 = 1 << 10,
|
||||||
|
BTN_R1 = 1 << 11,
|
||||||
|
BTN_L2 = 1 << 12,
|
||||||
|
BTN_R2 = 1 << 13,
|
||||||
|
BTN_MENU = 1 << 14,
|
||||||
|
BTN_VOL_UP = 1 << 15,
|
||||||
|
BTN_VOL_DN = 1 << 16,
|
||||||
|
BTN_POWER = 1 << 17,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: this belongs in defines.h or better yet a platform.h
|
||||||
|
#define BTN_RESUME BTN_X
|
||||||
|
#define BTN_SLEEP BTN_POWER
|
||||||
|
|
||||||
|
void PAD_reset(void);
|
||||||
|
void PAD_poll(void);
|
||||||
|
int PAD_anyPressed(void);
|
||||||
|
int PAD_justPressed(int btn);
|
||||||
|
int PAD_isPressed(int btn);
|
||||||
|
int PAD_justReleased(int btn);
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef __DEFS_H__
|
#ifndef __DEFINES_H__
|
||||||
#define __DEFS_H__
|
#define __DEFINES_H__
|
||||||
|
|
||||||
#define CODE_UP 0x5A
|
#define CODE_UP 0x5A
|
||||||
#define CODE_DOWN 0x5B
|
#define CODE_DOWN 0x5B
|
||||||
|
|
@ -25,18 +25,43 @@
|
||||||
#define BRIGHTNESS_MIN 0
|
#define BRIGHTNESS_MIN 0
|
||||||
#define BRIGHTNESS_MAX 10
|
#define BRIGHTNESS_MAX 10
|
||||||
|
|
||||||
#define SDCARD_PATH "/mnt/sdcard"
|
|
||||||
#define SYSTEM_PATH SDCARD_PATH "/.system/" PLATFORM
|
|
||||||
#define USERDATA_PATH SDCARD_PATH "/.userdata/" PLATFORM
|
|
||||||
#define MAX_PATH 512
|
#define MAX_PATH 512
|
||||||
|
|
||||||
|
#define SDCARD_PATH "/mnt/sdcard"
|
||||||
|
#define ROMS_PATH SDCARD_PATH "/Roms"
|
||||||
|
#define SYSTEM_PATH SDCARD_PATH "/.system/" PLATFORM
|
||||||
|
#define RES_PATH SDCARD_PATH "/.system/res"
|
||||||
|
#define FONT_PATH RES_PATH "/BPreplayBold-unhinted.otf"
|
||||||
|
#define USERDATA_PATH SDCARD_PATH "/.userdata/" PLATFORM
|
||||||
|
#define PAKS_PATH SYSTEM_PATH "/paks"
|
||||||
|
#define RECENT_PATH USERDATA_PATH "/.minui/recent.txt"
|
||||||
|
#define FAUX_RECENT_PATH SDCARD_PATH "/Recently Played"
|
||||||
|
#define COLLECTIONS_PATH SDCARD_PATH "/Collections"
|
||||||
|
|
||||||
|
#define LAST_PATH "/tmp/last.txt" // transient
|
||||||
|
#define CHANGE_DISC_PATH "/tmp/change_disc.txt"
|
||||||
|
#define RESUME_SLOT_PATH "/tmp/mmenu_slot.txt"
|
||||||
|
#define AUTO_RESUME_PATH USERDATA_PATH "/.miniui/auto_resume.txt"
|
||||||
|
#define AUTO_RESUME_SLOT "9"
|
||||||
|
#define ENABLE_SIMPLE_MODE_PATH USERDATA_PATH "/enable-simple-mode"
|
||||||
|
|
||||||
#define SCREEN_WIDTH 640
|
#define SCREEN_WIDTH 640
|
||||||
#define SCREEN_HEIGHT 480
|
#define SCREEN_HEIGHT 480
|
||||||
|
|
||||||
|
// GBA
|
||||||
|
// #define SCREEN_WIDTH 960
|
||||||
|
// #define SCREEN_HEIGHT 720
|
||||||
|
|
||||||
|
// GB converted to 4:3 with full height
|
||||||
|
// #define SCREEN_WIDTH 768
|
||||||
|
// #define SCREEN_HEIGHT 576
|
||||||
|
|
||||||
#define SCREEN_DEPTH 16
|
#define SCREEN_DEPTH 16
|
||||||
#define SCREEN_PITCH 1280
|
|
||||||
#define SCREEN_BPP 2
|
#define SCREEN_BPP 2
|
||||||
|
#define SCREEN_PITCH SCREEN_WIDTH * SCREEN_BPP
|
||||||
#define SCREEN_BUFFER_COUNT 3
|
#define SCREEN_BUFFER_COUNT 3
|
||||||
|
|
||||||
|
#define MAIN_ROW_COUNT 7
|
||||||
|
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
|
|
||||||
|
|
@ -46,4 +71,4 @@
|
||||||
#define MAX(a, b) (a) > (b) ? (a) : (b)
|
#define MAX(a, b) (a) > (b) ? (a) : (b)
|
||||||
#define MIN(a, b) (a) < (b) ? (a) : (b)
|
#define MIN(a, b) (a) < (b) ? (a) : (b)
|
||||||
|
|
||||||
#endif // __DEFS_H__
|
#endif
|
||||||
136
src/common/utils.c
Normal file
136
src/common/utils.c
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "utils.h"
|
||||||
|
#include "defines.h"
|
||||||
|
|
||||||
|
///////////////////////////////////////
|
||||||
|
|
||||||
|
int prefixMatch(char* pre, char* str) {
|
||||||
|
return (strncasecmp(pre,str,strlen(pre))==0);
|
||||||
|
}
|
||||||
|
int suffixMatch(char* suf, char* str) {
|
||||||
|
int len = strlen(suf);
|
||||||
|
int offset = strlen(str)-len;
|
||||||
|
return (offset>=0 && strncasecmp(suf, str+offset, len)==0);
|
||||||
|
}
|
||||||
|
int exactMatch(char* str1, char* str2) {
|
||||||
|
int len1 = strlen(str1);
|
||||||
|
if (len1!=strlen(str2)) return 0;
|
||||||
|
return (strncmp(str1,str2,len1)==0);
|
||||||
|
}
|
||||||
|
int hide(char* file_name) {
|
||||||
|
return file_name[0]=='.';
|
||||||
|
}
|
||||||
|
|
||||||
|
void getDisplayName(const char* in_name, char* out_name) {
|
||||||
|
char* tmp;
|
||||||
|
strcpy(out_name, in_name);
|
||||||
|
|
||||||
|
// extract just the filename if necessary
|
||||||
|
tmp = strrchr(in_name, '/');
|
||||||
|
if (tmp) strcpy(out_name, tmp+1);
|
||||||
|
|
||||||
|
// remove extension
|
||||||
|
tmp = strrchr(out_name, '.');
|
||||||
|
if (tmp && strlen(tmp)<=4) tmp[0] = '\0'; // 3 letter extension plus dot
|
||||||
|
|
||||||
|
// remove trailing parens (round and square)
|
||||||
|
char safe_name[256];
|
||||||
|
strcpy(safe_name,out_name);
|
||||||
|
while ((tmp=strrchr(out_name, '('))!=NULL || (tmp=strrchr(out_name, '['))!=NULL) {
|
||||||
|
if (tmp==out_name) break;
|
||||||
|
tmp[0] = '\0';
|
||||||
|
tmp = out_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we haven't nuked the entire name
|
||||||
|
if (out_name[0]=='\0') strcpy(out_name, safe_name);
|
||||||
|
|
||||||
|
// remove trailing whitespace
|
||||||
|
tmp = out_name + strlen(out_name) - 1;
|
||||||
|
while(tmp>out_name && isspace((unsigned char)*tmp)) tmp--;
|
||||||
|
tmp[1] = '\0';
|
||||||
|
}
|
||||||
|
void getEmuName(const char* in_name, char* out_name) { // NOTE: both char arrays need to be MAX_PATH length!
|
||||||
|
char* tmp;
|
||||||
|
strcpy(out_name, in_name);
|
||||||
|
tmp = out_name;
|
||||||
|
|
||||||
|
// extract just the Roms folder name if necessary
|
||||||
|
if (prefixMatch(ROMS_PATH, tmp)) {
|
||||||
|
tmp += strlen(ROMS_PATH) + 1;
|
||||||
|
char* tmp2 = strchr(tmp, '/');
|
||||||
|
if (tmp2) tmp2[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally extract pak name from parenths if present
|
||||||
|
tmp = strrchr(tmp, '(');
|
||||||
|
if (tmp) {
|
||||||
|
tmp += 1;
|
||||||
|
strcpy(out_name, tmp);
|
||||||
|
tmp = strchr(out_name,')');
|
||||||
|
tmp[0] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void normalizeNewline(char* line) {
|
||||||
|
int len = strlen(line);
|
||||||
|
if (len>1 && line[len-1]=='\n' && line[len-2]=='\r') { // windows!
|
||||||
|
line[len-2] = '\n';
|
||||||
|
line[len-1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void trimTrailingNewlines(char* line) {
|
||||||
|
int len = strlen(line);
|
||||||
|
while (len>0 && line[len-1]=='\n') {
|
||||||
|
line[len-1] = '\0'; // trim newline
|
||||||
|
len -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////
|
||||||
|
|
||||||
|
int exists(char* path) {
|
||||||
|
return access(path, F_OK)==0;
|
||||||
|
}
|
||||||
|
void touch(char* path) {
|
||||||
|
close(open(path, O_RDWR|O_CREAT, 0777));
|
||||||
|
}
|
||||||
|
void putFile(char* path, char* contents) {
|
||||||
|
FILE* file = fopen(path, "w");
|
||||||
|
if (file) {
|
||||||
|
fputs(contents, file);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void getFile(char* path, char* buffer, size_t buffer_size) {
|
||||||
|
FILE *file = fopen(path, "r");
|
||||||
|
if (file) {
|
||||||
|
fseek(file, 0L, SEEK_END);
|
||||||
|
size_t size = ftell(file);
|
||||||
|
if (size>buffer_size-1) size = buffer_size - 1;
|
||||||
|
rewind(file);
|
||||||
|
fread(buffer, sizeof(char), size, file);
|
||||||
|
fclose(file);
|
||||||
|
buffer[size] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int getInt(char* path) {
|
||||||
|
int i = 0;
|
||||||
|
FILE *file = fopen(path, "r");
|
||||||
|
if (file!=NULL) {
|
||||||
|
fscanf(file, "%i", &i);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
void putInt(char* path, int value) {
|
||||||
|
char buffer[8];
|
||||||
|
sprintf(buffer, "%d", value);
|
||||||
|
putFile(path, buffer);
|
||||||
|
}
|
||||||
22
src/common/utils.h
Normal file
22
src/common/utils.h
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
int prefixMatch(char* pre, char* str);
|
||||||
|
int suffixMatch(char* suf, char* str);
|
||||||
|
int exactMatch(char* str1, char* str2);
|
||||||
|
int hide(char* file_name);
|
||||||
|
|
||||||
|
void getDisplayName(const char* in_name, char* out_name);
|
||||||
|
void getEmuName(const char* in_name, char* out_name);
|
||||||
|
|
||||||
|
void normalizeNewline(char* line);
|
||||||
|
void trimTrailingNewlines(char* line);
|
||||||
|
|
||||||
|
int exists(char* path);
|
||||||
|
void touch(char* path);
|
||||||
|
void putFile(char* path, char* contents);
|
||||||
|
void getFile(char* path, char* buffer, size_t buffer_size);
|
||||||
|
void putInt(char* path, int value);
|
||||||
|
int getInt(char* path);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,386 +1,25 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <libgen.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <SDL/SDL.h>
|
#include <SDL/SDL.h>
|
||||||
#include <SDL/SDL_image.h>
|
#include <SDL/SDL_image.h>
|
||||||
#include <SDL/SDL_ttf.h>
|
#include <SDL/SDL_ttf.h>
|
||||||
|
|
||||||
#include <linux/fb.h>
|
#include <unistd.h>
|
||||||
#include <sys/ioctl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/mman.h>
|
#include <dlfcn.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include "libretro.h"
|
#include "libretro.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "api.h"
|
||||||
#include "scaler_neon.h"
|
#include "scaler_neon.h"
|
||||||
|
|
||||||
///////////////////////////////
|
static SDL_Surface* screen;
|
||||||
|
|
||||||
enum {
|
|
||||||
LOG_DEBUG = 0,
|
|
||||||
LOG_INFO,
|
|
||||||
LOG_WARN,
|
|
||||||
LOG_ERROR,
|
|
||||||
};
|
|
||||||
#define LOG_debug(fmt, ...) LOG_note(LOG_DEBUG, fmt, ##__VA_ARGS__)
|
|
||||||
#define LOG_info(fmt, ...) LOG_note(LOG_INFO, fmt, ##__VA_ARGS__)
|
|
||||||
#define LOG_warn(fmt, ...) LOG_note(LOG_WARN, fmt, ##__VA_ARGS__)
|
|
||||||
#define LOG_error(fmt, ...) LOG_note(LOG_ERROR, fmt, ##__VA_ARGS__)
|
|
||||||
void LOG_note(int level, const char* fmt, ...) {
|
|
||||||
char buf[1024] = {0};
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
vsnprintf(buf, sizeof(buf), fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
switch(level) {
|
|
||||||
#ifdef DEBUG
|
|
||||||
case LOG_DEBUG:
|
|
||||||
printf("DEBUG: %s", buf);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case LOG_INFO:
|
|
||||||
printf("INFO: %s", buf);
|
|
||||||
break;
|
|
||||||
case LOG_WARN:
|
|
||||||
fprintf(stderr, "WARN: %s", buf);
|
|
||||||
break;
|
|
||||||
case LOG_ERROR:
|
|
||||||
fprintf(stderr, "ERROR: %s", buf);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////
|
|
||||||
|
|
||||||
static struct GFX_Context {
|
|
||||||
int fb;
|
|
||||||
int pitch;
|
|
||||||
int buffer;
|
|
||||||
int buffer_size;
|
|
||||||
int map_size;
|
|
||||||
void* map;
|
|
||||||
struct fb_var_screeninfo vinfo;
|
|
||||||
struct fb_fix_screeninfo finfo;
|
|
||||||
|
|
||||||
SDL_Surface* screen;
|
|
||||||
} gfx;
|
|
||||||
SDL_Surface* GFX_init(void) {
|
|
||||||
SDL_Init(SDL_INIT_VIDEO);
|
|
||||||
|
|
||||||
// we're drawing to the (triple-buffered) framebuffer directly
|
|
||||||
// but we still need to set video mode to initialize input events
|
|
||||||
SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH, SDL_SWSURFACE);
|
|
||||||
SDL_ShowCursor(0);
|
|
||||||
|
|
||||||
// open framebuffer
|
|
||||||
gfx.fb = open("/dev/fb0", O_RDWR);
|
|
||||||
|
|
||||||
// configure framebuffer
|
|
||||||
ioctl(gfx.fb, FBIOGET_VSCREENINFO, &gfx.vinfo);
|
|
||||||
gfx.vinfo.bits_per_pixel = SCREEN_DEPTH;
|
|
||||||
gfx.vinfo.xres = SCREEN_WIDTH;
|
|
||||||
gfx.vinfo.yres = SCREEN_HEIGHT;
|
|
||||||
gfx.vinfo.xres_virtual = SCREEN_WIDTH;
|
|
||||||
gfx.vinfo.yres_virtual = SCREEN_HEIGHT * SCREEN_BUFFER_COUNT;
|
|
||||||
gfx.vinfo.xoffset = 0;
|
|
||||||
gfx.vinfo.activate = FB_ACTIVATE_VBL;
|
|
||||||
ioctl(gfx.fb, FBIOPUT_VSCREENINFO, &gfx.vinfo);
|
|
||||||
|
|
||||||
// get fixed screen info
|
|
||||||
ioctl(gfx.fb, FBIOGET_FSCREENINFO, &gfx.finfo);
|
|
||||||
gfx.map_size = gfx.finfo.smem_len;
|
|
||||||
gfx.map = mmap(0, gfx.map_size, PROT_READ | PROT_WRITE, MAP_SHARED, gfx.fb, 0);
|
|
||||||
|
|
||||||
// buffer tracking
|
|
||||||
gfx.buffer = 0;
|
|
||||||
gfx.buffer_size = SCREEN_PITCH * SCREEN_HEIGHT;
|
|
||||||
|
|
||||||
// return screen
|
|
||||||
gfx.screen = SDL_CreateRGBSurfaceFrom(gfx.map, SCREEN_WIDTH,SCREEN_HEIGHT, SCREEN_DEPTH,SCREEN_PITCH, 0,0,0,0);
|
|
||||||
return gfx.screen;
|
|
||||||
}
|
|
||||||
void GFX_clear(SDL_Surface* screen) {
|
|
||||||
memset(screen->pixels, 0, gfx.buffer_size);
|
|
||||||
}
|
|
||||||
void GFX_clearAll(void) {
|
|
||||||
memset(gfx.map, 0, gfx.map_size);
|
|
||||||
}
|
|
||||||
void GFX_flip(SDL_Surface* screen) {
|
|
||||||
// TODO: this would be moved to a thread
|
|
||||||
// I'm not clear on why that would be necessary
|
|
||||||
// if it's non-blocking and the pan will wait
|
|
||||||
// until the next vblank...
|
|
||||||
gfx.vinfo.yoffset = gfx.buffer * SCREEN_HEIGHT;
|
|
||||||
ioctl(gfx.fb, FBIOPAN_DISPLAY, &gfx.vinfo);
|
|
||||||
|
|
||||||
gfx.buffer += 1;
|
|
||||||
if (gfx.buffer>=SCREEN_BUFFER_COUNT) gfx.buffer -= SCREEN_BUFFER_COUNT;
|
|
||||||
screen->pixels = gfx.map + (gfx.buffer * gfx.buffer_size);
|
|
||||||
}
|
|
||||||
void GFX_quit(void) {
|
|
||||||
GFX_clearAll();
|
|
||||||
munmap(gfx.map, gfx.map_size);
|
|
||||||
close(gfx.fb);
|
|
||||||
SDL_Quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////
|
|
||||||
|
|
||||||
// based on picoarch's audio
|
|
||||||
// implementation, rewritten
|
|
||||||
// to understand it better
|
|
||||||
|
|
||||||
#define MAX_SAMPLE_RATE 48000
|
|
||||||
#define BATCH_SIZE 100
|
|
||||||
|
|
||||||
typedef struct SND_Frame {
|
|
||||||
int16_t left;
|
|
||||||
int16_t right;
|
|
||||||
} SND_Frame;
|
|
||||||
typedef int (*SND_Resampler)(const SND_Frame frame);
|
|
||||||
static struct SND_Context {
|
|
||||||
double frame_rate;
|
|
||||||
|
|
||||||
int sample_rate_in;
|
|
||||||
int sample_rate_out;
|
|
||||||
|
|
||||||
int buffer_seconds; // current_audio_buffer_size
|
|
||||||
SND_Frame* buffer; // buf
|
|
||||||
size_t frame_count; // buf_len
|
|
||||||
|
|
||||||
int frame_in; // buf_w
|
|
||||||
int frame_out; // buf_r
|
|
||||||
int frame_filled; // max_buf_w
|
|
||||||
|
|
||||||
SND_Resampler resample;
|
|
||||||
} snd;
|
|
||||||
void SND_audioCallback(void* userdata, uint8_t* stream, int len) { // plat_sound_callback
|
|
||||||
if (snd.frame_count==0) return;
|
|
||||||
|
|
||||||
int16_t *out = (int16_t *)stream;
|
|
||||||
len /= (sizeof(int16_t) * 2);
|
|
||||||
|
|
||||||
while (snd.frame_out!=snd.frame_in && len>0) {
|
|
||||||
*out++ = snd.buffer[snd.frame_out].left;
|
|
||||||
*out++ = snd.buffer[snd.frame_out].right;
|
|
||||||
|
|
||||||
snd.frame_filled = snd.frame_out;
|
|
||||||
|
|
||||||
snd.frame_out += 1;
|
|
||||||
len -= 1;
|
|
||||||
|
|
||||||
if (snd.frame_out>=snd.frame_count) snd.frame_out = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (len>0) {
|
|
||||||
*out++ = 0;
|
|
||||||
*out++ = 0;
|
|
||||||
len -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void SND_resizeBuffer(void) { // plat_sound_resize_buffer
|
|
||||||
snd.frame_count = snd.buffer_seconds * snd.sample_rate_in / snd.frame_rate;
|
|
||||||
if (snd.frame_count==0) return;
|
|
||||||
|
|
||||||
SDL_LockAudio();
|
|
||||||
|
|
||||||
int buffer_bytes = snd.frame_count * sizeof(SND_Frame);
|
|
||||||
snd.buffer = realloc(snd.buffer, buffer_bytes);
|
|
||||||
|
|
||||||
memset(snd.buffer, 0, buffer_bytes);
|
|
||||||
|
|
||||||
snd.frame_in = 0;
|
|
||||||
snd.frame_out = 0;
|
|
||||||
snd.frame_filled = snd.frame_count - 1;
|
|
||||||
|
|
||||||
SDL_UnlockAudio();
|
|
||||||
}
|
|
||||||
int SND_resampleNone(SND_Frame frame) { // audio_resample_passthrough
|
|
||||||
snd.buffer[snd.frame_in++] = frame;
|
|
||||||
if (snd.frame_in >= snd.frame_count) snd.frame_in = 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
int SND_resampleNear(SND_Frame frame) { // audio_resample_nearest
|
|
||||||
static int diff = 0;
|
|
||||||
int consumed = 0;
|
|
||||||
|
|
||||||
if (diff < snd.sample_rate_out) {
|
|
||||||
snd.buffer[snd.frame_in++] = frame;
|
|
||||||
if (snd.frame_in >= snd.frame_count) snd.frame_in = 0;
|
|
||||||
diff += snd.sample_rate_in;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diff >= snd.sample_rate_out) {
|
|
||||||
consumed++;
|
|
||||||
diff -= snd.sample_rate_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
void SND_selectResampler(void) { // plat_sound_select_resampler
|
|
||||||
if (snd.sample_rate_in==snd.sample_rate_out) {
|
|
||||||
snd.resample = SND_resampleNone;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
snd.resample = SND_resampleNear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
size_t SND_batchSamples(const SND_Frame* frames, size_t frame_count) { // plat_sound_write / plat_sound_write_resample
|
|
||||||
if (snd.frame_count==0) return 0;
|
|
||||||
|
|
||||||
SDL_LockAudio();
|
|
||||||
|
|
||||||
int consumed = 0;
|
|
||||||
while (frame_count > 0) {
|
|
||||||
int tries = 0;
|
|
||||||
int amount = MIN(BATCH_SIZE, frame_count);
|
|
||||||
|
|
||||||
while (tries < 10 && snd.frame_in==snd.frame_filled) {
|
|
||||||
tries++;
|
|
||||||
SDL_UnlockAudio();
|
|
||||||
SDL_Delay(1);
|
|
||||||
SDL_LockAudio();
|
|
||||||
}
|
|
||||||
|
|
||||||
while (amount && snd.frame_in != snd.frame_filled) {
|
|
||||||
consumed = snd.resample(*frames);
|
|
||||||
frames += consumed;
|
|
||||||
amount -= consumed;
|
|
||||||
frame_count -= consumed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SDL_UnlockAudio();
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SND_init(double sample_rate, double frame_rate) { // plat_sound_init
|
|
||||||
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
|
||||||
|
|
||||||
snd.frame_rate = frame_rate;
|
|
||||||
|
|
||||||
SDL_AudioSpec spec_in;
|
|
||||||
SDL_AudioSpec spec_out;
|
|
||||||
|
|
||||||
spec_in.freq = MIN(sample_rate, MAX_SAMPLE_RATE); // TODO: always MAX_SAMPLE_RATE on Miyoo Mini? use #ifdef PLATFORM_MIYOOMINI?
|
|
||||||
spec_in.format = AUDIO_S16;
|
|
||||||
spec_in.channels = 2;
|
|
||||||
spec_in.samples = 512;
|
|
||||||
spec_in.callback = SND_audioCallback;
|
|
||||||
|
|
||||||
SDL_OpenAudio(&spec_in, &spec_out);
|
|
||||||
|
|
||||||
snd.buffer_seconds = 5;
|
|
||||||
snd.sample_rate_in = sample_rate;
|
|
||||||
snd.sample_rate_out = spec_out.freq;
|
|
||||||
|
|
||||||
SND_selectResampler();
|
|
||||||
SND_resizeBuffer();
|
|
||||||
|
|
||||||
SDL_PauseAudio(0);
|
|
||||||
}
|
|
||||||
void SND_quit(void) { // plat_sound_finish
|
|
||||||
SDL_PauseAudio(1);
|
|
||||||
SDL_CloseAudio();
|
|
||||||
|
|
||||||
if (snd.buffer) {
|
|
||||||
free(snd.buffer);
|
|
||||||
snd.buffer = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////
|
|
||||||
|
|
||||||
enum {
|
|
||||||
BTN_NONE = 0,
|
|
||||||
BTN_UP = 1 << 0,
|
|
||||||
BTN_DOWN = 1 << 1,
|
|
||||||
BTN_LEFT = 1 << 2,
|
|
||||||
BTN_RIGHT = 1 << 3,
|
|
||||||
BTN_A = 1 << 4,
|
|
||||||
BTN_B = 1 << 5,
|
|
||||||
BTN_X = 1 << 6,
|
|
||||||
BTN_Y = 1 << 7,
|
|
||||||
BTN_START = 1 << 8,
|
|
||||||
BTN_SELECT = 1 << 9,
|
|
||||||
BTN_L1 = 1 << 10,
|
|
||||||
BTN_R1 = 1 << 11,
|
|
||||||
BTN_L2 = 1 << 12,
|
|
||||||
BTN_R2 = 1 << 13,
|
|
||||||
BTN_MENU = 1 << 14,
|
|
||||||
BTN_VOL_UP = 1 << 15,
|
|
||||||
BTN_VOL_DN = 1 << 16,
|
|
||||||
BTN_POWER = 1 << 17,
|
|
||||||
};
|
|
||||||
static struct PAD_Context {
|
|
||||||
int is_pressed;
|
|
||||||
int just_pressed;
|
|
||||||
int just_released;
|
|
||||||
} pad;
|
|
||||||
void PAD_poll(void) {
|
|
||||||
// reset transient state
|
|
||||||
pad.just_pressed = 0;
|
|
||||||
pad.just_released = 0;
|
|
||||||
|
|
||||||
// the actual poll
|
|
||||||
SDL_Event event;
|
|
||||||
while (SDL_PollEvent(&event)) {
|
|
||||||
int btn = BTN_NONE;
|
|
||||||
if (event.type==SDL_KEYDOWN || event.type==SDL_KEYUP) {
|
|
||||||
uint8_t code = event.key.keysym.scancode;
|
|
||||||
if (code==CODE_UP) btn = BTN_UP;
|
|
||||||
else if (code==CODE_DOWN) btn = BTN_DOWN;
|
|
||||||
else if (code==CODE_LEFT) btn = BTN_LEFT;
|
|
||||||
else if (code==CODE_RIGHT) btn = BTN_RIGHT;
|
|
||||||
else if (code==CODE_A) btn = BTN_A;
|
|
||||||
else if (code==CODE_B) btn = BTN_B;
|
|
||||||
else if (code==CODE_X) btn = BTN_X;
|
|
||||||
else if (code==CODE_Y) btn = BTN_Y;
|
|
||||||
else if (code==CODE_START) btn = BTN_START;
|
|
||||||
else if (code==CODE_SELECT) btn = BTN_SELECT;
|
|
||||||
else if (code==CODE_MENU) btn = BTN_MENU;
|
|
||||||
else if (code==CODE_L1) btn = BTN_L1;
|
|
||||||
else if (code==CODE_L2) btn = BTN_L2;
|
|
||||||
else if (code==CODE_R1) btn = BTN_R1;
|
|
||||||
else if (code==CODE_R2) btn = BTN_R2;
|
|
||||||
else if (code==CODE_VOL_UP) btn = BTN_VOL_UP;
|
|
||||||
else if (code==CODE_VOL_DN) btn = BTN_VOL_DN;
|
|
||||||
else if (code==CODE_POWER) btn = BTN_POWER;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (btn==BTN_NONE) continue;
|
|
||||||
|
|
||||||
if (event.type==SDL_KEYUP) {
|
|
||||||
pad.is_pressed &= ~btn; // unset
|
|
||||||
pad.just_released |= btn; // set
|
|
||||||
}
|
|
||||||
else if ((pad.is_pressed & btn)==BTN_NONE) {
|
|
||||||
pad.just_pressed |= btn; // set
|
|
||||||
pad.is_pressed |= btn; // set
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: switch to macros? not if I want to move it to a separate file
|
|
||||||
int PAD_anyPressed(void) { return pad.is_pressed!=BTN_NONE; }
|
|
||||||
int PAD_justPressed(int btn) { return pad.just_pressed & btn; }
|
|
||||||
int PAD_isPressed(int btn) { return pad.is_pressed & btn; }
|
|
||||||
int PAD_justReleased(int btn) { return pad.just_released & btn; }
|
|
||||||
|
|
||||||
// #define PAD_anyPressed() (pad.is_pressed!=BTN_NONE)
|
|
||||||
// #define PAD_justPressed(btn) (pad.just_pressed & (btn))
|
|
||||||
// #define PAD_isPressed(btn) (pad.is_pressed & (btn))
|
|
||||||
// #define PAD_justReleased(btn) (pad.just_released & (btn))
|
|
||||||
|
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
|
|
||||||
|
|
@ -460,6 +99,7 @@ static void SRAM_read(void) {
|
||||||
|
|
||||||
char filename[MAX_PATH];
|
char filename[MAX_PATH];
|
||||||
SRAM_getPath(filename);
|
SRAM_getPath(filename);
|
||||||
|
printf("sav path (read): %s\n", filename);
|
||||||
|
|
||||||
FILE *sram_file = fopen(filename, "r");
|
FILE *sram_file = fopen(filename, "r");
|
||||||
if (!sram_file) return;
|
if (!sram_file) return;
|
||||||
|
|
@ -478,6 +118,7 @@ static void SRAM_write(void) {
|
||||||
|
|
||||||
char filename[MAX_PATH];
|
char filename[MAX_PATH];
|
||||||
SRAM_getPath(filename);
|
SRAM_getPath(filename);
|
||||||
|
printf("sav path (write): %s\n", filename);
|
||||||
|
|
||||||
FILE *sram_file = fopen(filename, "w");
|
FILE *sram_file = fopen(filename, "w");
|
||||||
if (!sram_file) {
|
if (!sram_file) {
|
||||||
|
|
@ -615,6 +256,7 @@ static bool environment_callback(unsigned cmd, void *data) { // copied from pico
|
||||||
// TODO: core.tag isn't available at this point
|
// TODO: core.tag isn't available at this point
|
||||||
// TODO: it only becomes available after we open the game...
|
// TODO: it only becomes available after we open the game...
|
||||||
sprintf(sys_dir, SDCARD_PATH "/.userdata/%s/%s-%s", PLATFORM, core.tag, core.name);
|
sprintf(sys_dir, SDCARD_PATH "/.userdata/%s/%s-%s", PLATFORM, core.tag, core.name);
|
||||||
|
puts(sys_dir); fflush(stdout);
|
||||||
*out = sys_dir;
|
*out = sys_dir;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -752,9 +394,13 @@ static bool environment_callback(unsigned cmd, void *data) { // copied from pico
|
||||||
for (int i=0; vars[i].key; i++) {
|
for (int i=0; vars[i].key; i++) {
|
||||||
const struct retro_core_option_definition *var = &vars[i];
|
const struct retro_core_option_definition *var = &vars[i];
|
||||||
// printf("set key: %s to value: %s (%s)\n", var->key, var->default_value, var->desc);
|
// printf("set key: %s to value: %s (%s)\n", var->key, var->default_value, var->desc);
|
||||||
printf("set core (intl) key: %s to value: %s\n", var->key, var->default_value);
|
char *default_value = (char*)var->default_value;
|
||||||
|
if (!strcmp("gpsp_save_method", var->key)) {
|
||||||
|
default_value = "libretro"; // TODO: tmp, patch or override gpsp
|
||||||
|
}
|
||||||
|
printf("set core (intl) key: %s to value: %s\n", var->key, default_value);
|
||||||
strcpy(tmp_options[i].key, var->key);
|
strcpy(tmp_options[i].key, var->key);
|
||||||
strcpy(tmp_options[i].value, var->default_value);
|
strcpy(tmp_options[i].value, default_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -1019,6 +665,9 @@ static void scale(const void* src, int width, int height, int pitch, void* dst)
|
||||||
|
|
||||||
dst += (oy * SCREEN_PITCH) + (ox * SCREEN_BPP);
|
dst += (oy * SCREEN_PITCH) + (ox * SCREEN_BPP);
|
||||||
|
|
||||||
|
// TODO: trying to identify source of the framepacing issue
|
||||||
|
// scale1x(width,height,pitch,src,dst);
|
||||||
|
|
||||||
switch (scale) {
|
switch (scale) {
|
||||||
case 4: scale4x_n16((void*)src,dst,width,height,pitch,SCREEN_PITCH); break;
|
case 4: scale4x_n16((void*)src,dst,width,height,pitch,SCREEN_PITCH); break;
|
||||||
case 3: scale3x_n16((void*)src,dst,width,height,pitch,SCREEN_PITCH); break;
|
case 3: scale3x_n16((void*)src,dst,width,height,pitch,SCREEN_PITCH); break;
|
||||||
|
|
@ -1032,6 +681,52 @@ static void scale(const void* src, int width, int height, int pitch, void* dst)
|
||||||
// case 2: scale2x(width,height,pitch,src,dst); break;
|
// case 2: scale2x(width,height,pitch,src,dst); break;
|
||||||
// default: scale1x(width,height,pitch,src,dst); break;
|
// default: scale1x(width,height,pitch,src,dst); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: diagnosing framepacing issues
|
||||||
|
if (1) {
|
||||||
|
static int frame = 0;
|
||||||
|
int w = 8;
|
||||||
|
int h = 16;
|
||||||
|
int fps = 60;
|
||||||
|
int x = frame * w;
|
||||||
|
|
||||||
|
dst -= (oy * SCREEN_PITCH) + (ox * SCREEN_BPP);
|
||||||
|
|
||||||
|
dst += (SCREEN_WIDTH - (w * fps)) / 2 * SCREEN_BPP;
|
||||||
|
|
||||||
|
void* _dst = dst;
|
||||||
|
memset(_dst, 0, (h * SCREEN_PITCH));
|
||||||
|
for (int y=0; y<h; y++) {
|
||||||
|
memset(_dst-SCREEN_BPP, 0xff, SCREEN_BPP);
|
||||||
|
memset(_dst+(w * fps * SCREEN_BPP), 0xff, SCREEN_BPP);
|
||||||
|
_dst += SCREEN_PITCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst += (x * SCREEN_BPP);
|
||||||
|
|
||||||
|
for (int y=0; y<h; y++) {
|
||||||
|
memset(dst, 0xff, w * SCREEN_BPP);
|
||||||
|
dst += SCREEN_PITCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame += 1;
|
||||||
|
if (frame>=fps) frame -= fps;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0) {
|
||||||
|
// measure framerate
|
||||||
|
static int start = -1;
|
||||||
|
static int ticks = 0;
|
||||||
|
ticks += 1;
|
||||||
|
int now = SDL_GetTicks();
|
||||||
|
if (start==-1) start = now;
|
||||||
|
if (now-start>=1000) {
|
||||||
|
start = now;
|
||||||
|
printf("fps: %i\n", ticks);
|
||||||
|
fflush(stdout);
|
||||||
|
ticks = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void video_refresh_callback(const void *data, unsigned width, unsigned height, size_t pitch) {
|
static void video_refresh_callback(const void *data, unsigned width, unsigned height, size_t pitch) {
|
||||||
|
|
@ -1043,8 +738,8 @@ static void video_refresh_callback(const void *data, unsigned width, unsigned he
|
||||||
last_height = height;
|
last_height = height;
|
||||||
GFX_clearAll();
|
GFX_clearAll();
|
||||||
}
|
}
|
||||||
scale(data,width,height,pitch,gfx.screen->pixels);
|
scale(data,width,height,pitch,screen->pixels);
|
||||||
GFX_flip(gfx.screen);
|
// GFX_flip(screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_sample_callback(int16_t left, int16_t right) {
|
static void audio_sample_callback(int16_t left, int16_t right) {
|
||||||
|
|
@ -1198,53 +893,19 @@ void Core_close(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc , char* argv[]) {
|
int main(int argc , char* argv[]) {
|
||||||
// system("touch /tmp/wait");
|
char core_path[MAX_PATH];
|
||||||
|
char rom_path[MAX_PATH];
|
||||||
// char* core_path = "/mnt/sdcard/.system/rg35xx/cores/gambatte_libretro.so";
|
char tag_name[MAX_PATH];
|
||||||
// char* rom_path = "/mnt/sdcard/Roms/Game Boy Color (GBC)/Legend of Zelda, The - Link's Awakening DX (USA, Europe) (Rev 2) (SGB Enhanced) (GB Compatible).gbc";
|
|
||||||
// char* rom_path = "/mnt/sdcard/Roms/Game Boy Color (GBC)/Dragon Warrior I & II (USA) (SGB Enhanced).gbc";
|
|
||||||
// char* tag_name = "GBC";
|
|
||||||
// char* rom_path = "/mnt/sdcard/Roms/Game Boy (GB)/Super Mario Land (World) (Rev A).gb";
|
|
||||||
// char* rom_path = "/mnt/sdcard/Roms/Game Boy (GB)/Dr. Mario (World).gb";
|
|
||||||
// char* tag_name = "GB";
|
|
||||||
|
|
||||||
// char* core_path = "/mnt/sdcard/.system/rg35xx/cores/gpsp_libretro.so";
|
strcpy(core_path, argv[1]);
|
||||||
// char* rom_path = "/mnt/sdcard/Roms/Game Boy Advance (GBA)/Metroid Zero Mission.gba";
|
strcpy(rom_path, argv[2]);
|
||||||
// char* tag_name = "GBA";
|
getEmuName(rom_path, tag_name);
|
||||||
|
|
||||||
// char* core_path = "/mnt/sdcard/.system/rg35xx/cores/fceumm_libretro.so";
|
|
||||||
// char* rom_path = "/mnt/sdcard/Roms/Nintendo (FC)/Castlevania 3 - Dracula's Curse (U).nes";
|
|
||||||
// char* rom_path = "/mnt/sdcard/Roms/Nintendo (FC)/Mega Man 2 (U).nes";
|
|
||||||
// char* tag_name = "FC";
|
|
||||||
|
|
||||||
// char* core_path = "/mnt/sdcard/.system/rg35xx/cores/picodrive_libretro.so";
|
|
||||||
// char* rom_path = "/mnt/sdcard/Roms/Genesis (MD)/Sonic The Hedgehog (USA, Europe).md";
|
|
||||||
// char* tag_name = "MD";
|
|
||||||
|
|
||||||
char* core_path = "/mnt/sdcard/.system/rg35xx/cores/snes9x2005_plus_libretro.so";
|
|
||||||
// char* rom_path = "/mnt/sdcard/Roms/Super Nintendo (SFC)/Super Mario World (USA).sfc";
|
|
||||||
// char* rom_path = "/mnt/sdcard/Roms/Super Nintendo (SFC)/Super Mario World 2 - Yoshi's Island (USA, Asia) (Rev 1).sfc";
|
|
||||||
char* rom_path = "/mnt/sdcard/Roms/Super Nintendo (SFC)/Final Fantasy III (USA) (Rev 1).sfc";
|
|
||||||
char* tag_name = "SFC";
|
|
||||||
|
|
||||||
// char* core_path = "/mnt/sdcard/.system/rg35xx/cores/pcsx_rearmed_libretro.so";
|
|
||||||
// char* rom_path = "/mnt/sdcard/Roms/PlayStation (PS)/Castlevania - Symphony of the Night (USA)/Castlevania - Symphony of the Night (USA).cue";
|
|
||||||
// char* rom_path = "/mnt/sdcard/Roms/PlayStation (PS)/Final Fantasy VII (USA)/Final Fantasy VII (USA).m3u";
|
|
||||||
// char* tag_name = "PS";
|
|
||||||
|
|
||||||
// char* core_path = "/mnt/sdcard/.system/rg35xx/cores/pokemini_libretro.so";
|
|
||||||
// char* rom_path = "/mnt/sdcard/Roms/Pokémon mini (PKM)/Pokemon Tetris (Europe) (En,Ja,Fr).min";
|
|
||||||
// char* tag_name = "PKM";
|
|
||||||
|
|
||||||
// char core_path[MAX_PATH]; strcpy(core_path, argv[1]);
|
|
||||||
// char rom_path[MAX_PATH]; strcpy(rom_path, argv[2]);
|
|
||||||
// char tag_name[MAX_PATH]; strcpy(tag_name, argv[3]);
|
|
||||||
|
|
||||||
LOG_info("core_path: %s\n", core_path);
|
LOG_info("core_path: %s\n", core_path);
|
||||||
LOG_info("rom_path: %s\n", rom_path);
|
LOG_info("rom_path: %s\n", rom_path);
|
||||||
LOG_info("tag_name: %s\n", tag_name);
|
LOG_info("tag_name: %s\n", tag_name);
|
||||||
|
|
||||||
SDL_Surface* screen = GFX_init();
|
screen = GFX_init();
|
||||||
Core_open(core_path, tag_name); LOG_info("after Core_open\n");
|
Core_open(core_path, tag_name); LOG_info("after Core_open\n");
|
||||||
Core_init(); LOG_info("after Core_init\n");
|
Core_init(); LOG_info("after Core_init\n");
|
||||||
Game_open(rom_path); LOG_info("after Game_open\n");
|
Game_open(rom_path); LOG_info("after Game_open\n");
|
||||||
|
|
@ -1252,16 +913,23 @@ int main(int argc , char* argv[]) {
|
||||||
SND_init(core.sample_rate, core.fps); LOG_info("after SND_init\n");
|
SND_init(core.sample_rate, core.fps); LOG_info("after SND_init\n");
|
||||||
State_read(); LOG_info("after State_read\n");
|
State_read(); LOG_info("after State_read\n");
|
||||||
|
|
||||||
while (1) {
|
int start = SDL_GetTicks();
|
||||||
|
while (1) {
|
||||||
|
unsigned long frame_start = SDL_GetTicks();
|
||||||
|
|
||||||
if (PAD_justPressed(BTN_POWER)) {
|
if (PAD_justPressed(BTN_POWER)) {
|
||||||
system("rm /tmp/minui_exec");
|
// system("rm /tmp/minui_exec");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// still not working
|
// still not working
|
||||||
// if (PAD_justPressed(BTN_L1)) State_read();
|
// if (PAD_justPressed(BTN_L1)) State_read();
|
||||||
// else if (PAD_justPressed(BTN_R1)) State_write();
|
// else if (PAD_justPressed(BTN_R1)) State_write();
|
||||||
core.run();
|
core.run();
|
||||||
|
|
||||||
|
unsigned long frame_duration = SDL_GetTicks() - frame_start;
|
||||||
|
#define TARGET_FRAME_DURATION 15
|
||||||
|
if (frame_duration<TARGET_FRAME_DURATION) SDL_Delay(TARGET_FRAME_DURATION-frame_duration);
|
||||||
|
GFX_flip(screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
Game_close();
|
Game_close();
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,6 @@ CFLAGS += -I. -I../common -I./libretro-common/include -DPLATFORM=\"$(UNION_PLAT
|
||||||
LDFLAGS = -ldl -lSDL -lSDL_image -lSDL_ttf -lmsettings
|
LDFLAGS = -ldl -lSDL -lSDL_image -lSDL_ttf -lmsettings
|
||||||
|
|
||||||
all:
|
all:
|
||||||
$(CC) main.c ../common/scaler_neon.c -o $(TARGET) $(CFLAGS) $(LDFLAGS)
|
$(CC) main.c ../common/scaler_neon.c ../common/utils.c ../common/api.c -o $(TARGET) $(CFLAGS) $(LDFLAGS)
|
||||||
clean:
|
clean:
|
||||||
rm -f $(TARGET)
|
rm -f $(TARGET)
|
||||||
1569
src/minui/main.c
Normal file
1569
src/minui/main.c
Normal file
File diff suppressed because it is too large
Load diff
15
src/minui/makefile
Normal file
15
src/minui/makefile
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
ifeq (,$(CROSS_COMPILE))
|
||||||
|
$(error missing CROSS_COMPILE for this toolchain)
|
||||||
|
endif
|
||||||
|
|
||||||
|
TARGET = minui.elf
|
||||||
|
|
||||||
|
CC = $(CROSS_COMPILE)gcc
|
||||||
|
CFLAGS = -marm -mtune=cortex-a9 -mfpu=neon-vfpv4 -mfloat-abi=hard -march=armv7-a -fomit-frame-pointer
|
||||||
|
CFLAGS += -I. -I../common -DPLATFORM=\"$(UNION_PLATFORM)\"
|
||||||
|
LDFLAGS = -ldl -lSDL -lSDL_image -lSDL_ttf -lmsettings
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(CC) main.c ../common/utils.c ../common/api.c -o $(TARGET) $(CFLAGS) $(LDFLAGS)
|
||||||
|
clean:
|
||||||
|
rm -f $(TARGET)
|
||||||
5
src/test/Test.pak/launch.sh
Executable file
5
src/test/Test.pak/launch.sh
Executable file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
cd $(dirname "$0")
|
||||||
|
|
||||||
|
./test.elf &> "$SDCARD_PATH/log.txt"
|
||||||
87
src/test/main.c
Normal file
87
src/test/main.c
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <SDL/SDL.h>
|
||||||
|
#include <SDL/SDL_image.h>
|
||||||
|
#include <SDL/SDL_ttf.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "api.h"
|
||||||
|
|
||||||
|
///////////////////////////////////////
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
static uint64_t GFX_getTicks(void) {
|
||||||
|
uint64_t ret;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
|
||||||
|
ret = (uint64_t)tv.tv_sec * 1000000;
|
||||||
|
ret += (uint64_t)tv.tv_usec;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char *argv[]) {
|
||||||
|
SDL_Surface* screen = GFX_init();
|
||||||
|
PAD_reset();
|
||||||
|
|
||||||
|
int quit = 0;
|
||||||
|
while (!quit) {
|
||||||
|
// unsigned long frame_start = SDL_GetTicks();
|
||||||
|
uint64_t frame_start_us = GFX_getTicks();
|
||||||
|
|
||||||
|
PAD_poll();
|
||||||
|
if (PAD_anyPressed()) break;
|
||||||
|
|
||||||
|
// TODO: diagnosing framepacing issues
|
||||||
|
static int frame = 0;
|
||||||
|
int x = frame * 8;
|
||||||
|
|
||||||
|
void* dst;
|
||||||
|
|
||||||
|
dst = screen->pixels;
|
||||||
|
memset(dst, (frame%2)?0x00:0xff, (SCREEN_HEIGHT * SCREEN_PITCH));
|
||||||
|
// memset(dst, 0, (16 * SCREEN_PITCH));
|
||||||
|
// for (int y=0; y<16; y++) {
|
||||||
|
// memset(dst+(8 * 60 * SCREEN_BPP), 0xff, SCREEN_BPP);
|
||||||
|
// dst += SCREEN_PITCH;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// dst = screen->pixels;
|
||||||
|
// dst += (x * SCREEN_BPP);
|
||||||
|
//
|
||||||
|
// for (int y=0; y<16; y++) {
|
||||||
|
// memset(dst, 0xff, 8 * SCREEN_BPP);
|
||||||
|
// dst += SCREEN_PITCH;
|
||||||
|
// }
|
||||||
|
|
||||||
|
frame += 1;
|
||||||
|
if (frame>=60) frame -= 60;
|
||||||
|
|
||||||
|
GFX_flip(screen);
|
||||||
|
|
||||||
|
// SDL_Delay(500);
|
||||||
|
|
||||||
|
// slow down to 60fps
|
||||||
|
// unsigned long frame_duration = SDL_GetTicks() - frame_start;
|
||||||
|
uint64_t frame_duration_us = GFX_getTicks() - frame_start_us;
|
||||||
|
|
||||||
|
// #define TARGET_FRAME_DURATION 17
|
||||||
|
#define TARGET_FRAME_DURATION_US 16667
|
||||||
|
// if (frame_duration<TARGET_FRAME_DURATION) SDL_Delay(TARGET_FRAME_DURATION-frame_duration);
|
||||||
|
if (frame_duration_us<TARGET_FRAME_DURATION_US) usleep(TARGET_FRAME_DURATION_US-frame_duration_us);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_FreeSurface(screen);
|
||||||
|
GFX_quit();
|
||||||
|
}
|
||||||
15
src/test/makefile
Normal file
15
src/test/makefile
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
ifeq (,$(CROSS_COMPILE))
|
||||||
|
$(error missing CROSS_COMPILE for this toolchain)
|
||||||
|
endif
|
||||||
|
|
||||||
|
TARGET = Test.pak/test.elf
|
||||||
|
|
||||||
|
CC = $(CROSS_COMPILE)gcc
|
||||||
|
CFLAGS = -marm -mtune=cortex-a9 -mfpu=neon-vfpv4 -mfloat-abi=hard -march=armv7-a -fomit-frame-pointer
|
||||||
|
CFLAGS += -I. -I../common -DPLATFORM=\"$(UNION_PLATFORM)\"
|
||||||
|
LDFLAGS = -ldl -lSDL -lSDL_image -lSDL_ttf
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(CC) main.c ../common/utils.c ../common/api.c -o $(TARGET) $(CFLAGS) $(LDFLAGS)
|
||||||
|
clean:
|
||||||
|
rm -f $(TARGET)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue