mirror of
https://github.com/libretro/potator.git
synced 2024-11-23 08:29:41 +00:00
Add emscripten, shortcuts
Added: * New palettes. * Ghosting (reduce flickering). * Emscripten. * Const update rate 60. * Drag & drop. * Gamepad support. * Keyboard Shortcuts: reset, palette, window size, ghosting, mute audio. Fixes: * Screen X offset (from MAME). Example: 'Bubble World'. * Revert cycles (there was a problem with the pause in 'Hero Kid'). * Loading a rom after loaded 32K rom. * Save/load savestate (new extension is '.svst').
This commit is contained in:
parent
b9ba068a26
commit
932d57a895
@ -23,7 +23,7 @@ void controls_state_write(uint8 type, uint8 data)
|
||||
return;
|
||||
else
|
||||
controls_state = 0;
|
||||
|
||||
|
||||
if (type)
|
||||
controls_state |= data;
|
||||
else
|
||||
|
266
common/gpu.c
266
common/gpu.c
@ -8,19 +8,64 @@
|
||||
#ifdef NDS
|
||||
#include <nds.h>
|
||||
#endif
|
||||
|
||||
#ifdef _ODSDL_
|
||||
#include "../platform/opendingux/shared.h"
|
||||
#endif
|
||||
|
||||
static uint16 *supervision_palette;
|
||||
static uint8 gpu_regs[4];
|
||||
#ifdef NDS
|
||||
#define RGB555(R,G,B) ((((int)(B))<<10)|(((int)(G))<<5)|(((int)(R)))|(1<<15))
|
||||
#else
|
||||
#define RGB555(R,G,B) ((((int)(B))<<10)|(((int)(G))<<5)|(((int)(R))))
|
||||
#endif
|
||||
|
||||
const static uint8 palettes[COLOUR_SCHEME_COUNT][12] = {
|
||||
{
|
||||
252, 252, 252,
|
||||
168, 168, 168,
|
||||
84, 84, 84,
|
||||
0, 0, 0,
|
||||
},
|
||||
{
|
||||
252, 154, 0,
|
||||
168, 102, 0,
|
||||
84, 51, 0,
|
||||
0, 0, 0,
|
||||
},
|
||||
{
|
||||
50, 227, 50,
|
||||
34, 151, 34,
|
||||
17, 76, 17,
|
||||
0, 0, 0,
|
||||
},
|
||||
{
|
||||
0, 154, 252,
|
||||
0, 102, 168,
|
||||
0, 51, 84,
|
||||
0, 0, 0,
|
||||
},
|
||||
{
|
||||
224, 248, 208,
|
||||
136, 192, 112,
|
||||
52, 104, 86,
|
||||
8, 24, 32,
|
||||
},
|
||||
{
|
||||
0x70, 0xb0, 0x78,
|
||||
0x48, 0x98, 0x90,
|
||||
0x24, 0x58, 0x60,
|
||||
0x08, 0x24, 0x30,
|
||||
},
|
||||
};
|
||||
|
||||
static uint16 *supervision_palette;
|
||||
|
||||
#define SB_MAX (GHOSTING_MAX + 1)
|
||||
static int ghostCount = 0;
|
||||
static uint8 *screenBuffers[SB_MAX];
|
||||
static uint8 screenBufferStartX[SB_MAX];
|
||||
|
||||
static void add_ghosting(uint32 scanline, uint16 *backbuffer, uint8 start_x, uint8 end_x);
|
||||
|
||||
void gpu_init(void)
|
||||
{
|
||||
supervision_palette = (uint16*)malloc(4 * sizeof(int16));
|
||||
@ -28,101 +73,42 @@ void gpu_init(void)
|
||||
|
||||
void gpu_reset(void)
|
||||
{
|
||||
#if defined(GP2X)
|
||||
supervision_palette[3] = gp2x_video_RGB_color16( 0, 0, 0);
|
||||
supervision_palette[2] = gp2x_video_RGB_color16( 85, 85, 85);
|
||||
supervision_palette[1] = gp2x_video_RGB_color16(170,170,170);
|
||||
supervision_palette[0] = gp2x_video_RGB_color16(170,170,170);
|
||||
#elif defined(NDS)
|
||||
supervision_palette[3] = RGB555( 0, 0, 0);
|
||||
supervision_palette[2] = RGB555(10,10,10);
|
||||
supervision_palette[1] = RGB555(20,20,20);
|
||||
supervision_palette[0] = RGB555(30,30,30);
|
||||
#elif defined(_ODSDL_)
|
||||
supervision_palette[3] = PIX_TO_RGB(actualScreen->format, 0, 0, 0);
|
||||
supervision_palette[2] = PIX_TO_RGB(actualScreen->format, 85, 85, 85);
|
||||
supervision_palette[1] = PIX_TO_RGB(actualScreen->format, 170, 170, 170);
|
||||
supervision_palette[0] = PIX_TO_RGB(actualScreen->format, 240, 240, 240);
|
||||
#else
|
||||
supervision_palette[3] = RGB555( 0, 0, 0);
|
||||
supervision_palette[2] = RGB555(10,10,10);
|
||||
supervision_palette[1] = RGB555(20,20,20);
|
||||
supervision_palette[0] = RGB555(30,30,30);
|
||||
#endif
|
||||
|
||||
memset(gpu_regs, 0, 4);
|
||||
gpu_set_colour_scheme(COLOUR_SCHEME_DEFAULT);
|
||||
gpu_set_ghosting(0);
|
||||
}
|
||||
|
||||
void gpu_set_colour_scheme(int colourScheme)
|
||||
{
|
||||
float greenf = 1;
|
||||
float bluef = 1;
|
||||
float redf = 1;
|
||||
|
||||
switch (colourScheme) {
|
||||
case COLOUR_SCHEME_DEFAULT:
|
||||
break;
|
||||
case COLOUR_SCHEME_AMBER:
|
||||
greenf = 0.61f;
|
||||
bluef = 0.00f;
|
||||
redf = 1.00f;
|
||||
break;
|
||||
case COLOUR_SCHEME_GREEN:
|
||||
greenf = 0.90f;
|
||||
bluef = 0.20f;
|
||||
redf = 0.20f;
|
||||
break;
|
||||
case COLOUR_SCHEME_BLUE:
|
||||
greenf = 0.30f;
|
||||
bluef = 0.75f;
|
||||
redf = 0.30f;
|
||||
break;
|
||||
default:
|
||||
colourScheme = 0;
|
||||
break;
|
||||
float c[12];
|
||||
int i;
|
||||
for (i = 0; i < 12; i++) {
|
||||
c[i] = palettes[colourScheme][i] / 255.0f;
|
||||
}
|
||||
#if defined(GP2X)
|
||||
supervision_palette[3] = gp2x_video_RGB_color16( 0*redf, 0*greenf, 0*bluef);
|
||||
supervision_palette[2] = gp2x_video_RGB_color16( 85*redf, 85*greenf, 85*bluef);
|
||||
supervision_palette[1] = gp2x_video_RGB_color16(170*redf,170*greenf,170*bluef);
|
||||
supervision_palette[0] = gp2x_video_RGB_color16(255*redf,255*greenf,255*bluef);
|
||||
#elif defined(NDS)
|
||||
supervision_palette[3] = RGB555( 0*redf, 0*greenf, 0*bluef);
|
||||
supervision_palette[2] = RGB555(10*redf,10*greenf,10*bluef);
|
||||
supervision_palette[1] = RGB555(20*redf,20*greenf,20*bluef);
|
||||
supervision_palette[0] = RGB555(30*redf,30*greenf,30*bluef);
|
||||
supervision_palette[3] = gp2x_video_RGB_color16(255*c[9],255*c[10],255*c[11]);
|
||||
supervision_palette[2] = gp2x_video_RGB_color16(255*c[6],255*c[ 7],255*c[ 8]);
|
||||
supervision_palette[1] = gp2x_video_RGB_color16(255*c[3],255*c[ 4],255*c[ 5]);
|
||||
supervision_palette[0] = gp2x_video_RGB_color16(255*c[0],255*c[ 1],255*c[ 2]);
|
||||
#elif defined(_ODSDL_)
|
||||
int p11 = (int)85 * redf; int p12 = (int)85 * greenf; int p13 = (int)85 * bluef;
|
||||
int p21 = (int)170 * redf; int p22 = (int)170 * greenf; int p23 = (int)170 * bluef;
|
||||
int p31 = (int)255 * redf; int p32 = (int)255 * greenf; int p33 = (int)255 * bluef;
|
||||
supervision_palette[3] = PIX_TO_RGB(actualScreen->format, 0, 0, 0);
|
||||
supervision_palette[2] = PIX_TO_RGB(actualScreen->format, p11, p12, p13);
|
||||
supervision_palette[1] = PIX_TO_RGB(actualScreen->format, p21, p22, p23);
|
||||
supervision_palette[0] = PIX_TO_RGB(actualScreen->format, p31, p32, p33);
|
||||
supervision_palette[3] = PIX_TO_RGB(actualScreen->format, (int)(255*c[9]),(int)(255*c[10]),(int)(255*c[11]));
|
||||
supervision_palette[2] = PIX_TO_RGB(actualScreen->format, (int)(255*c[6]),(int)(255*c[ 7]),(int)(255*c[ 8]));
|
||||
supervision_palette[1] = PIX_TO_RGB(actualScreen->format, (int)(255*c[3]),(int)(255*c[ 4]),(int)(255*c[ 5]));
|
||||
supervision_palette[0] = PIX_TO_RGB(actualScreen->format, (int)(255*c[0]),(int)(255*c[ 1]),(int)(255*c[ 2]));
|
||||
#else
|
||||
supervision_palette[3] = RGB555( 0*redf, 0*greenf, 0*bluef);
|
||||
supervision_palette[2] = RGB555(10*redf,10*greenf,10*bluef);
|
||||
supervision_palette[1] = RGB555(20*redf,20*greenf,20*bluef);
|
||||
supervision_palette[0] = RGB555(30*redf,30*greenf,30*bluef);
|
||||
supervision_palette[3] = RGB555(31*c[9],31*c[10],31*c[11]);
|
||||
supervision_palette[2] = RGB555(31*c[6],31*c[ 7],31*c[ 8]);
|
||||
supervision_palette[1] = RGB555(31*c[3],31*c[ 4],31*c[ 5]);
|
||||
supervision_palette[0] = RGB555(31*c[0],31*c[ 1],31*c[ 2]);
|
||||
#endif
|
||||
}
|
||||
|
||||
void gpu_write(uint32 addr, uint8 data)
|
||||
{
|
||||
gpu_regs[(addr&0x03)] = data;
|
||||
}
|
||||
|
||||
uint8 gpu_read(uint32 addr)
|
||||
{
|
||||
return gpu_regs[(addr&0x03)];
|
||||
}
|
||||
|
||||
void gpu_render_scanline(uint32 scanline, uint16 *backbuffer)
|
||||
{
|
||||
uint8 *vram_line = &(memorymap_getUpperRamPointer())[(gpu_regs[2] >> 2) + (scanline*0x30)];
|
||||
uint8 *m_reg = memorymap_getRegisters();
|
||||
uint8 *vram_line = memorymap_getUpperRamPointer() + (m_reg[XPOS] / 4 + m_reg[YPOS] * 0x30) + (scanline * 0x30);
|
||||
uint8 x;
|
||||
|
||||
for (x =0; x < 160; x += 4)
|
||||
for (x = 0; x < 160; x += 4)
|
||||
{
|
||||
uint8 b = *(vram_line++);
|
||||
backbuffer[3] = supervision_palette[((b >> 6) & 0x03)];
|
||||
@ -135,18 +121,110 @@ void gpu_render_scanline(uint32 scanline, uint16 *backbuffer)
|
||||
|
||||
void gpu_render_scanline_fast(uint32 scanline, uint16 *backbuffer)
|
||||
{
|
||||
uint8 *vram_line = &(memorymap_getUpperRamPointer())[scanline];
|
||||
uint8 x;
|
||||
uint32 *buf = (uint32 *)backbuffer;
|
||||
uint8 *vram_line = memorymap_getUpperRamPointer() + scanline;
|
||||
uint8 x, j, b;
|
||||
|
||||
uint8 *m_reg = memorymap_getRegisters();
|
||||
int start_x = 3 - (m_reg[XPOS] & 3);
|
||||
int end_x = (163 < (m_reg[XSIZE] | 3) ? 163 : (m_reg[XSIZE] | 3));
|
||||
//if (start_x != 3) printf("start_x = %d\n", start_x);
|
||||
//if (end_x != 163) printf("end_x = %d\n", end_x);
|
||||
for (x = start_x; x < end_x; x += 4) {
|
||||
uint8 b = *(vram_line++);
|
||||
*(buf++) = (supervision_palette[((b >> 2) & 0x03)] << 16) | (supervision_palette[((b >> 0) & 0x03)]);
|
||||
*(buf++) = (supervision_palette[((b >> 6) & 0x03)] << 16) | (supervision_palette[((b >> 4) & 0x03)]);
|
||||
uint8 start_x = m_reg[XPOS] & 3; //3 - (m_reg[XPOS] & 3);
|
||||
uint8 end_x = (163 < (m_reg[XSIZE] | 3) ? 163 : (m_reg[XSIZE] | 3)) - 3; //(163 < (m_reg[XSIZE] | 3) ? 163 : (m_reg[XSIZE] | 3));
|
||||
//if (start_x != 0) printf("start_x = %u\n", start_x);
|
||||
//if (end_x != 160) printf("end_x = %u\n", end_x);
|
||||
j = start_x;
|
||||
|
||||
// #1
|
||||
if (j & 3) {
|
||||
b = *vram_line++;
|
||||
b >>= (j & 3) * 2;
|
||||
}
|
||||
for (x = 0; x < end_x; x++, j++) {
|
||||
if (!(j & 3)) {
|
||||
b = *(vram_line++);
|
||||
}
|
||||
backbuffer[x] = supervision_palette[b & 3];
|
||||
b >>= 2;
|
||||
}
|
||||
// #2
|
||||
/*for (x = 0; x < end_x; x++, j++) {
|
||||
b = vram_line[j >> 2];
|
||||
backbuffer[x] = supervision_palette[(b >> ((j & 3) * 2)) & 3];
|
||||
}*/
|
||||
|
||||
if (ghostCount != 0) {
|
||||
add_ghosting(scanline, backbuffer, start_x, end_x);
|
||||
}
|
||||
}
|
||||
|
||||
void gpu_set_ghosting(int frameCount)
|
||||
{
|
||||
int i;
|
||||
if (frameCount < 0)
|
||||
ghostCount = 0;
|
||||
else if (frameCount > GHOSTING_MAX)
|
||||
ghostCount = GHOSTING_MAX;
|
||||
else
|
||||
ghostCount = frameCount;
|
||||
|
||||
if (ghostCount != 0) {
|
||||
if (screenBuffers[0] == NULL) {
|
||||
for (i = 0; i < SB_MAX; i++) {
|
||||
screenBuffers[i] = malloc(160 * 160 / 4);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < SB_MAX; i++) {
|
||||
memset(screenBuffers[i], 0, 160 * 160 / 4);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < SB_MAX; i++) {
|
||||
free(screenBuffers[i]);
|
||||
screenBuffers[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void add_ghosting(uint32 scanline, uint16 *backbuffer, uint8 start_x, uint8 end_x)
|
||||
{
|
||||
static int curSB = 0;
|
||||
static int lineCount = 0;
|
||||
|
||||
uint8 *vram_line = memorymap_getUpperRamPointer() + scanline;
|
||||
uint8 x, i, j;
|
||||
|
||||
screenBufferStartX[curSB] = start_x;
|
||||
memset(screenBuffers[curSB] + lineCount * 160 / 4, 0, 160 / 4);
|
||||
for (j = start_x, x = 0; x < end_x; x++, j++) {
|
||||
uint8 b = vram_line[j >> 2];
|
||||
int pixInd = (x + lineCount * 160) / 4;
|
||||
uint8 innerInd = (j & 3) * 2;
|
||||
uint8 c = (b >> innerInd) & 3;
|
||||
if (c == 0) {
|
||||
for (i = 0; i < ghostCount; i++) {
|
||||
uint8 sbInd = (curSB + (SB_MAX - 1) - i) % SB_MAX;
|
||||
innerInd = ((screenBufferStartX[sbInd] + x) & 3) * 2;
|
||||
c = (screenBuffers[sbInd][pixInd] >> innerInd) & 3;
|
||||
if (c != 0) {
|
||||
#if defined(GP2X) || defined(_ODSDL_)
|
||||
backbuffer[x] = supervision_palette[3 - 3 * i / ghostCount];
|
||||
#else
|
||||
uint8 r = (supervision_palette[c] >> 0) & 31;
|
||||
uint8 g = (supervision_palette[c] >> 5) & 31;
|
||||
uint8 b = (supervision_palette[c] >> 10) & 31;
|
||||
r = r + (((supervision_palette[0] >> 0) & 31) - r) * i / ghostCount;
|
||||
g = g + (((supervision_palette[0] >> 5) & 31) - g) * i / ghostCount;
|
||||
b = b + (((supervision_palette[0] >> 10) & 31) - b) * i / ghostCount;
|
||||
backbuffer[x] = RGB555(r, g, b);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
screenBuffers[curSB][pixInd] |= c << innerInd;
|
||||
}
|
||||
}
|
||||
|
||||
if (lineCount == 159) {
|
||||
curSB = (curSB + 1) % SB_MAX;
|
||||
}
|
||||
lineCount = (lineCount + 1) % 160;
|
||||
}
|
||||
|
16
common/gpu.h
16
common/gpu.h
@ -3,12 +3,24 @@
|
||||
|
||||
#include "supervision.h"
|
||||
|
||||
enum
|
||||
{
|
||||
COLOUR_SCHEME_DEFAULT,
|
||||
COLOUR_SCHEME_AMBER,
|
||||
COLOUR_SCHEME_GREEN,
|
||||
COLOUR_SCHEME_BLUE,
|
||||
COLOUR_SCHEME_BGB,
|
||||
COLOUR_SCHEME_YOUTUBE,
|
||||
COLOUR_SCHEME_COUNT
|
||||
};
|
||||
|
||||
#define GHOSTING_MAX 8
|
||||
|
||||
void gpu_init(void);
|
||||
void gpu_reset(void);
|
||||
void gpu_set_colour_scheme(int colourScheme);
|
||||
void gpu_write(uint32 addr, uint8 data);
|
||||
uint8 gpu_read(uint32 addr);
|
||||
void gpu_render_scanline(uint32 scanline, uint16 *backbuffer);
|
||||
void gpu_render_scanline_fast(uint32 scanline, uint16 *backbuffer);
|
||||
void gpu_set_ghosting(int frameCount);
|
||||
|
||||
#endif
|
||||
|
@ -13,45 +13,25 @@
|
||||
/** changes to this file. **/
|
||||
/*************************************************************/
|
||||
|
||||
/* FIXME: need to add cycle data for 65C02 - uso. */
|
||||
static byte Cycles[256] =
|
||||
{
|
||||
//7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,
|
||||
//2,5,2,8,4,4,6,6,2,4,2,7,5,5,7,7,
|
||||
//6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,
|
||||
//2,5,2,8,4,4,6,6,2,4,2,7,5,5,7,7,
|
||||
//6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,
|
||||
//2,5,2,8,4,4,6,6,2,4,2,7,5,5,7,7,
|
||||
//6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,
|
||||
//2,5,2,8,4,4,6,6,2,4,2,7,5,5,7,7,
|
||||
//2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
|
||||
//2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,
|
||||
//2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
|
||||
//2,5,2,5,4,4,4,4,2,4,2,5,4,4,4,4,
|
||||
//2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,
|
||||
//2,5,2,8,4,4,6,6,2,4,2,7,5,5,7,7,
|
||||
//2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,
|
||||
//2,5,2,8,4,4,6,6,2,4,2,7,5,5,7,7
|
||||
|
||||
//https://github.com/mamedev/historic-mess/blob/master/src/emu/cpu/m6502/t6502.c
|
||||
//https://github.com/mamedev/historic-mess/blob/master/src/emu/cpu/m6502/t65c02.c
|
||||
//0 1 2 3 4 5 6 7 8 9 a b c d e f
|
||||
7,6,2,8,3,3,5,5,3,2,2,2,2,4,6,5, // 0
|
||||
2,5,3,8,3,4,6,5,2,4,2,7,4,4,7,5, // 1
|
||||
6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,5, // 2
|
||||
2,5,3,8,4,4,6,5,2,4,2,7,4,4,7,5, // 3
|
||||
6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,5, // 4
|
||||
2,5,3,8,4,4,6,5,2,4,3,7,5,4,7,5, // 5
|
||||
6,6,2,8,2,3,5,5,4,2,2,2,5,4,6,5, // 6
|
||||
2,5,3,8,4,4,6,5,2,4,4,7,2,4,7,5, // 7
|
||||
2,6,2,6,3,3,3,5,2,2,2,2,4,4,4,5, // 8
|
||||
2,6,4,6,4,4,4,5,2,5,2,5,4,5,5,5, // 9
|
||||
2,6,2,6,3,3,3,5,2,2,2,2,4,4,4,5, // a
|
||||
2,5,3,5,4,4,4,5,2,4,2,5,4,4,4,5, // b
|
||||
2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,5, // c
|
||||
2,5,3,8,4,4,6,5,2,4,3,7,5,4,7,5, // d
|
||||
2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,5, // e
|
||||
2,5,3,8,4,4,6,5,2,4,4,7,5,4,7,5 // f
|
||||
//0 1 2 3 4 5 6 7 8 9 a b c d e f
|
||||
7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,
|
||||
2,5,2,8,4,4,6,6,2,4,2,7,5,5,7,7,
|
||||
6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,
|
||||
2,5,2,8,4,4,6,6,2,4,2,7,5,5,7,7,
|
||||
6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,
|
||||
2,5,2,8,4,4,6,6,2,4,2,7,5,5,7,7,
|
||||
6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,
|
||||
2,5,2,8,4,4,6,6,2,4,2,7,5,5,7,7,
|
||||
2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
|
||||
2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,
|
||||
2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,
|
||||
2,5,2,5,4,4,4,4,2,4,2,5,4,4,4,4,
|
||||
2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,
|
||||
2,5,2,8,4,4,6,6,2,4,2,7,5,5,7,7,
|
||||
2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,
|
||||
2,5,2,8,4,4,6,6,2,4,2,7,5,5,7,7
|
||||
};
|
||||
|
||||
byte ZNTable[256] =
|
||||
|
@ -65,7 +65,6 @@ uint8 memorymap_registers_read(uint32 Addr)
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
break;
|
||||
//return gpu_read(Addr);
|
||||
case 0x20:
|
||||
return controls_read();
|
||||
case 0x21:
|
||||
@ -101,7 +100,6 @@ void memorymap_registers_write(uint32 Addr, uint8 Value)
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
gpu_write(Addr, Value);
|
||||
break;
|
||||
case 0x23:
|
||||
timer_write(Value);
|
||||
@ -114,19 +112,19 @@ void memorymap_registers_write(uint32 Addr, uint8 Value)
|
||||
return;
|
||||
case 0x10: case 0x11: case 0x12: case 0x13:
|
||||
case 0x14: case 0x15: case 0x16: case 0x17:
|
||||
soundport_w(((Addr & 0x4) >> 2), Addr & 3, Value);
|
||||
sound_soundport_w(((Addr & 0x4) >> 2), Addr & 3, Value);
|
||||
break;
|
||||
case 0x28:
|
||||
case 0x29:
|
||||
case 0x2a:
|
||||
svision_noise_w(Addr & 0x07, Value);
|
||||
sound_noise_w(Addr & 0x07, Value);
|
||||
break;
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
case 0x1a:
|
||||
case 0x1b:
|
||||
case 0x1c:
|
||||
svision_sounddma_w(Addr & 0x07, Value);
|
||||
sound_sounddma_w(Addr & 0x07, Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -180,16 +178,17 @@ byte Rd6502(register word Addr)
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
void memorymap_load(uint8 *rom, uint32 size)
|
||||
void memorymap_load(uint8 **rom, uint32 size)
|
||||
{
|
||||
memorymap_programRomSize = size;
|
||||
memorymap_programRom = rom;
|
||||
memorymap_programRom = *rom;
|
||||
|
||||
if (memorymap_programRomSize == 32768) {
|
||||
uint8 *tmp = (uint8 *)malloc(0x10000);
|
||||
memcpy(tmp + 0x0000, memorymap_programRom, 0x8000);
|
||||
memcpy(tmp + 0x8000, memorymap_programRom, 0x8000);
|
||||
free(memorymap_programRom);
|
||||
*rom = tmp;
|
||||
memorymap_programRom = tmp;
|
||||
memorymap_programRomSize = 0x10000;
|
||||
}
|
||||
@ -224,3 +223,35 @@ uint8 *memorymap_getRomPointer(void)
|
||||
{
|
||||
return memorymap_programRom;
|
||||
}
|
||||
|
||||
void memorymap_save_state(FILE *fp)
|
||||
{
|
||||
fwrite(memorymap_regs, 1, 0x2000, fp);
|
||||
fwrite(memorymap_lowerRam, 1, 0x2000, fp);
|
||||
fwrite(memorymap_upperRam, 1, 0x2000, fp);
|
||||
|
||||
uint32 offset = 0;
|
||||
offset = memorymap_lowerRomBank - memorymap_programRom;
|
||||
fwrite(&offset, 1, sizeof(offset), fp);
|
||||
offset = memorymap_upperRomBank - memorymap_programRom;
|
||||
fwrite(&offset, 1, sizeof(offset), fp);
|
||||
|
||||
fwrite(&dma_finished, 1, sizeof(dma_finished), fp);
|
||||
fwrite(&timer_shot, 1, sizeof(timer_shot), fp);
|
||||
}
|
||||
|
||||
void memorymap_load_state(FILE *fp)
|
||||
{
|
||||
fread(memorymap_regs, 1, 0x2000, fp);
|
||||
fread(memorymap_lowerRam, 1, 0x2000, fp);
|
||||
fread(memorymap_upperRam, 1, 0x2000, fp);
|
||||
|
||||
uint32 offset = 0;
|
||||
fread(&offset, 1, sizeof(offset), fp);
|
||||
memorymap_lowerRomBank = memorymap_programRom + offset;
|
||||
fread(&offset, 1, sizeof(offset), fp);
|
||||
memorymap_upperRomBank = memorymap_programRom + offset;
|
||||
|
||||
fread(&dma_finished, 1, sizeof(dma_finished), fp);
|
||||
fread(&timer_shot, 1, sizeof(timer_shot), fp);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define __MEMORYMAP_H__
|
||||
|
||||
#include "supervision.h"
|
||||
#include <stdio.h>
|
||||
|
||||
enum
|
||||
{
|
||||
@ -18,7 +19,10 @@ void memorymap_init(void);
|
||||
void memorymap_reset(void);
|
||||
uint8 memorymap_registers_read(uint32 Addr);
|
||||
void memorymap_registers_write(uint32 Addr, uint8 Value);
|
||||
void memorymap_load(uint8 *rom, uint32 size);
|
||||
void memorymap_load(uint8 **rom, uint32 size);
|
||||
|
||||
void memorymap_save_state(FILE *fp);
|
||||
void memorymap_load_state(FILE *fp);
|
||||
|
||||
uint8 *memorymap_getUpperRamPointer(void);
|
||||
uint8 *memorymap_getLowerRamPointer(void);
|
||||
|
@ -40,6 +40,13 @@ typedef struct {
|
||||
} SVISION_DMA;
|
||||
SVISION_DMA m_dma;
|
||||
|
||||
void sound_reset(void)
|
||||
{
|
||||
memset(m_channel, 0, sizeof(m_channel));
|
||||
memset(&m_noise, 0, sizeof(m_noise) );
|
||||
memset(&m_dma, 0, sizeof(m_dma) );
|
||||
}
|
||||
|
||||
void sound_stream_update(uint8 *stream, int len)
|
||||
{
|
||||
int i, j;
|
||||
@ -48,14 +55,14 @@ void sound_stream_update(uint8 *stream, int len)
|
||||
uint8 *left = stream + 0;
|
||||
uint8 *right = stream + 1;
|
||||
|
||||
for (i = 0; i < len >> 1; i++, left+=2, right+=2) {
|
||||
for (i = 0; i < len >> 1; i++, left += 2, right += 2) {
|
||||
s = 0;
|
||||
*left = *right = 0;
|
||||
|
||||
for (channel = m_channel, j = 0; j < 2; j++, channel++) {
|
||||
if (channel->size != 0) {
|
||||
if (channel->on || channel->count) {
|
||||
int on = FALSE;
|
||||
BOOL on = FALSE;
|
||||
switch (channel->waveform) {
|
||||
case 0:
|
||||
on = channel->pos <= (28 * channel->size) >> 5;
|
||||
@ -71,7 +78,7 @@ void sound_stream_update(uint8 *stream, int len)
|
||||
on = channel->pos <= (9 * channel->size) >> 5;
|
||||
break;
|
||||
}
|
||||
s = on ? channel->volume : 0; // << 8 : 0;
|
||||
s = on ? channel->volume /*<< 8*/ : 0;
|
||||
if (j == 0) {
|
||||
*right += s;
|
||||
}
|
||||
@ -87,13 +94,13 @@ void sound_stream_update(uint8 *stream, int len)
|
||||
|
||||
if (m_noise.on && (m_noise.play || m_noise.count)) {
|
||||
s = (m_noise.value ? 1 /*<< 8*/ : 0) * m_noise.volume;
|
||||
int b1, b2;
|
||||
if (m_noise.left)
|
||||
*left += s;
|
||||
if (m_noise.right)
|
||||
*right += s;
|
||||
m_noise.pos += m_noise.step;
|
||||
if (m_noise.pos >= 1.0) {
|
||||
BOOL b1, b2;
|
||||
switch (m_noise.type) {
|
||||
case SVISION_NOISE_Type7Bit:
|
||||
m_noise.value = m_noise.state & 0x40 ? 1 : 0;
|
||||
@ -152,7 +159,7 @@ void sound_decrement(void)
|
||||
m_noise.count--;
|
||||
}
|
||||
|
||||
void soundport_w(int which, int offset, int data)
|
||||
void sound_soundport_w(int which, int offset, int data)
|
||||
{
|
||||
SVISION_CHANNEL *channel = &m_channel[which];
|
||||
unsigned short size;
|
||||
@ -182,7 +189,7 @@ void soundport_w(int which, int offset, int data)
|
||||
}
|
||||
}
|
||||
|
||||
void svision_sounddma_w(int offset, int data)
|
||||
void sound_sounddma_w(int offset, int data)
|
||||
{
|
||||
m_dma.reg[offset] = data;
|
||||
switch (offset) {
|
||||
@ -208,7 +215,7 @@ void svision_sounddma_w(int offset, int data)
|
||||
}
|
||||
}
|
||||
|
||||
void svision_noise_w(int offset, int data)
|
||||
void sound_noise_w(int offset, int data)
|
||||
{
|
||||
m_noise.reg[offset] = data;
|
||||
switch (offset) {
|
||||
|
@ -5,14 +5,15 @@
|
||||
|
||||
#define BPS 44100
|
||||
|
||||
void sound_reset(void);
|
||||
/*!
|
||||
* Generate U8, 2 channels.
|
||||
* \param len in bytes.
|
||||
*/
|
||||
void sound_stream_update(uint8 *stream, int len);
|
||||
void sound_decrement(void);
|
||||
void soundport_w(int which, int offset, int data);
|
||||
void svision_sounddma_w(int offset, int data);
|
||||
void svision_noise_w(int offset, int data);
|
||||
void sound_soundport_w(int which, int offset, int data);
|
||||
void sound_sounddma_w(int offset, int data);
|
||||
void sound_noise_w(int offset, int data);
|
||||
|
||||
#endif
|
||||
|
@ -13,21 +13,17 @@
|
||||
|
||||
#include "./m6502/m6502.h"
|
||||
|
||||
#define COLOUR_SCHEME_DEFAULT 0
|
||||
#define COLOUR_SCHEME_AMBER 1
|
||||
#define COLOUR_SCHEME_GREEN 2
|
||||
#define COLOUR_SCHEME_BLUE 3
|
||||
|
||||
void supervision_init(void);
|
||||
void supervision_reset(void);
|
||||
void supervision_done(void);
|
||||
void supervision_exec(uint16 *backbuffer);
|
||||
BOOL supervision_load(uint8 *rom, uint32 romSize);
|
||||
BOOL supervision_load(uint8 **rom, uint32 romSize);
|
||||
BOOL supervision_update_input(void);
|
||||
void supervision_set_colour_scheme(int ws_colourScheme);
|
||||
void supervision_set_colour_scheme(int colourScheme);
|
||||
void supervision_set_ghosting(int frameCount);
|
||||
M6502 *supervision_get6502regs(void);
|
||||
|
||||
int sv_loadState(const char *statepath, int id);
|
||||
int sv_saveState(const char *statepath, int id);
|
||||
int supervision_save_state(const char *statepath, int id);
|
||||
int supervision_load_state(const char *statepath, int id);
|
||||
|
||||
#endif
|
||||
|
@ -31,3 +31,15 @@ void timer_exec(uint32 cycles)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void timer_save_state(FILE *fp)
|
||||
{
|
||||
fwrite(&timer_cycles, 1, sizeof(timer_cycles), fp);
|
||||
fwrite(&timer_activated, 1, sizeof(timer_activated), fp);
|
||||
}
|
||||
|
||||
void timer_load_state(FILE *fp)
|
||||
{
|
||||
fread(&timer_cycles, 1, sizeof(timer_cycles), fp);
|
||||
fread(&timer_activated, 1, sizeof(timer_activated), fp);
|
||||
}
|
||||
|
@ -2,9 +2,13 @@
|
||||
#define __TIMER_H__
|
||||
|
||||
#include "supervision.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void timer_reset(void);
|
||||
void timer_write(uint8 data);
|
||||
void timer_exec(uint32 cycles);
|
||||
|
||||
void timer_save_state(FILE *fp);
|
||||
void timer_load_state(FILE *fp);
|
||||
|
||||
#endif
|
||||
|
151
common/watara.c
151
common/watara.c
@ -19,7 +19,6 @@
|
||||
#endif
|
||||
|
||||
static M6502 m6502_registers;
|
||||
|
||||
static BOOL irq = FALSE;
|
||||
|
||||
void m6502_set_irq_line(BOOL assertLine)
|
||||
@ -39,27 +38,19 @@ byte Loop6502(register M6502 *R)
|
||||
|
||||
void supervision_init(void)
|
||||
{
|
||||
memorymap_init();
|
||||
gpu_init();
|
||||
}
|
||||
|
||||
BOOL supervision_load(uint8 *rom, uint32 romSize)
|
||||
{
|
||||
memorymap_load(rom, romSize);
|
||||
supervision_reset();
|
||||
|
||||
return TRUE;
|
||||
memorymap_init();
|
||||
}
|
||||
|
||||
void supervision_reset(void)
|
||||
{
|
||||
memorymap_reset();
|
||||
gpu_reset();
|
||||
timer_reset();
|
||||
controls_reset();
|
||||
gpu_reset();
|
||||
memorymap_reset();
|
||||
sound_reset();
|
||||
timer_reset();
|
||||
|
||||
Reset6502(&m6502_registers);
|
||||
|
||||
irq = FALSE;
|
||||
}
|
||||
|
||||
@ -67,14 +58,12 @@ void supervision_done(void)
|
||||
{
|
||||
}
|
||||
|
||||
void supervision_set_colour_scheme(int sv_colourScheme)
|
||||
BOOL supervision_load(uint8 **rom, uint32 romSize)
|
||||
{
|
||||
gpu_set_colour_scheme(sv_colourScheme);
|
||||
}
|
||||
memorymap_load(rom, romSize);
|
||||
supervision_reset();
|
||||
|
||||
M6502 *supervision_get6502regs(void)
|
||||
{
|
||||
return &m6502_registers;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL supervision_update_input(void)
|
||||
@ -82,23 +71,39 @@ BOOL supervision_update_input(void)
|
||||
return controls_update();
|
||||
}
|
||||
|
||||
void supervision_set_colour_scheme(int colourScheme)
|
||||
{
|
||||
gpu_set_colour_scheme(colourScheme);
|
||||
}
|
||||
|
||||
void supervision_set_ghosting(int frameCount)
|
||||
{
|
||||
gpu_set_ghosting(frameCount);
|
||||
}
|
||||
|
||||
M6502 *supervision_get6502regs(void)
|
||||
{
|
||||
return &m6502_registers;
|
||||
}
|
||||
|
||||
void supervision_exec(uint16 *backbuffer)
|
||||
{
|
||||
uint32 supervision_scanline, scan = 0;
|
||||
|
||||
uint8 *m_reg = memorymap_getRegisters();
|
||||
//if (!((m_reg[BANK] >> 3) & 1)) { printf("ndraw "); }
|
||||
scan = m_reg[XPOS] / 4 + m_reg[YPOS] * 0x30;
|
||||
|
||||
for (supervision_scanline = 0; supervision_scanline < 160; supervision_scanline++)
|
||||
{
|
||||
m6502_registers.ICount = 512;
|
||||
for (supervision_scanline = 0; supervision_scanline < 160; supervision_scanline++) {
|
||||
m6502_registers.ICount = 512;
|
||||
timer_exec(m6502_registers.ICount);
|
||||
|
||||
Run6502(&m6502_registers);
|
||||
}
|
||||
|
||||
//if (!((m_reg[BANK] >> 3) & 1)) { printf("LCD off\n"); }
|
||||
scan = m_reg[XPOS] / 4 + m_reg[YPOS] * 0x30;
|
||||
for (supervision_scanline = 0; supervision_scanline < 160; supervision_scanline++) {
|
||||
#ifdef NDS
|
||||
gpu_render_scanline(supervision_scanline, backbuffer);
|
||||
backbuffer += 160+96;
|
||||
backbuffer += 160 + 96;
|
||||
#else
|
||||
//gpu_render_scanline(supervision_scanline, backbuffer);
|
||||
gpu_render_scanline_fast(scan, backbuffer);
|
||||
@ -109,84 +114,70 @@ void supervision_exec(uint16 *backbuffer)
|
||||
scan = 0; // SSSnake
|
||||
}
|
||||
|
||||
if (Rd6502(0x2026)&0x01)
|
||||
if (Rd6502(0x2026) & 0x01)
|
||||
Int6502(supervision_get6502regs(), INT_NMI);
|
||||
|
||||
sound_decrement();
|
||||
}
|
||||
|
||||
int sv_loadState(const char *statepath, int id)
|
||||
int supervision_save_state(const char *statepath, int id)
|
||||
{
|
||||
FILE *fp;
|
||||
char newPath[256];
|
||||
|
||||
strcpy(newPath, statepath);
|
||||
sprintf(newPath + strlen(newPath) - 3, ".s%d", id);
|
||||
sprintf(newPath + strlen(newPath), ".svst");
|
||||
|
||||
#ifdef GP2X
|
||||
gp2x_printf(0,10,220,"newPath = %s",newPath);
|
||||
gp2x_video_RGB_flip(0);
|
||||
#endif
|
||||
#ifdef NDS
|
||||
iprintf("\nnewPath = %s",newPath);
|
||||
#endif
|
||||
|
||||
fp = fopen(newPath, "rb");
|
||||
|
||||
if (fp) {
|
||||
fread(&m6502_registers, 1, sizeof(m6502_registers), fp);
|
||||
fread(memorymap_programRom, 1, sizeof(memorymap_programRom), fp);
|
||||
fread(memorymap_lowerRam, 1, 0x2000, fp);
|
||||
fread(memorymap_upperRam, 1, 0x2000, fp);
|
||||
fread(memorymap_lowerRomBank, 1, sizeof(memorymap_lowerRomBank), fp);
|
||||
fread(memorymap_upperRomBank, 1, sizeof(memorymap_upperRomBank), fp);
|
||||
fread(memorymap_regs, 1, 0x2000, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
#ifdef GP2X
|
||||
sleep(1);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sv_saveState(const char *statepath, int id)
|
||||
{
|
||||
FILE *fp;
|
||||
char newPath[256];
|
||||
|
||||
strcpy(newPath, statepath);
|
||||
sprintf(newPath + strlen(newPath) - 3, ".s%d", id);
|
||||
|
||||
#ifdef GP2X
|
||||
gp2x_printf(0,10,220,"newPath = %s",newPath);
|
||||
gp2x_video_RGB_flip(0);
|
||||
#endif
|
||||
#ifdef NDS
|
||||
iprintf("\nnewPath = %s",newPath);
|
||||
#endif
|
||||
|
||||
fp = fopen(newPath, "wb");
|
||||
|
||||
if (fp) {
|
||||
fwrite(&m6502_registers, 1, sizeof(m6502_registers), fp);
|
||||
fwrite(memorymap_programRom, 1, sizeof(memorymap_programRom), fp);
|
||||
fwrite(memorymap_lowerRam, 1, 0x2000, fp);
|
||||
fwrite(memorymap_upperRam, 1, 0x2000, fp);
|
||||
fwrite(memorymap_lowerRomBank, 1, sizeof(memorymap_lowerRomBank), fp);
|
||||
fwrite(memorymap_upperRomBank, 1, sizeof(memorymap_upperRomBank), fp);
|
||||
fwrite(memorymap_regs, 1, 0x2000, fp);
|
||||
fwrite(&m6502_registers, 1, sizeof(m6502_registers), fp);
|
||||
fwrite(&irq, 1, sizeof(irq), fp);
|
||||
|
||||
memorymap_save_state(fp);
|
||||
timer_save_state(fp);
|
||||
|
||||
fflush(fp);
|
||||
fclose(fp);
|
||||
#ifdef GP2X
|
||||
sync();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef GP2X
|
||||
sleep(1);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int supervision_load_state(const char *statepath, int id)
|
||||
{
|
||||
FILE *fp;
|
||||
char newPath[256];
|
||||
|
||||
strcpy(newPath, statepath);
|
||||
sprintf(newPath + strlen(newPath), ".svst");
|
||||
|
||||
#ifdef GP2X
|
||||
gp2x_video_RGB_flip(0);
|
||||
#endif
|
||||
|
||||
fp = fopen(newPath, "rb");
|
||||
if (fp) {
|
||||
sound_reset();
|
||||
|
||||
fread(&m6502_registers, 1, sizeof(m6502_registers), fp);
|
||||
fread(&irq, 1, sizeof(irq), fp);
|
||||
|
||||
memorymap_load_state(fp);
|
||||
timer_load_state(fp);
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
#ifdef GP2X
|
||||
sleep(1);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
if(romname!=NULL){
|
||||
loadROM(romname);
|
||||
supervision_load((uint8*)buffer, (uint32)buffer_size);
|
||||
supervision_load(&buffer, (uint32)buffer_size);
|
||||
} else {
|
||||
handleFileMenu(); // File menu
|
||||
}
|
||||
|
@ -269,7 +269,7 @@ void handleFileMenu(void)
|
||||
RESIZE();
|
||||
loadROM(FileList[curFile + virtualFile].fName);
|
||||
textClear();
|
||||
supervision_load((uint8*)buffer, (uint32)buffer_size);
|
||||
supervision_load(&buffer, (uint32)buffer_size);
|
||||
textClear();
|
||||
return;
|
||||
}
|
||||
@ -496,8 +496,8 @@ void handleMainMenu(void)
|
||||
case MMOPTION_RESTART: RESIZE(); supervision_reset(); textClear(); return;
|
||||
case MMOPTION_SELECTOR: handleFileMenu(); return;
|
||||
case MMOPTION_OPTIONS: handleOptionsMenu(); textClear(); return;
|
||||
case MMOPTION_SAVESTATE: sv_saveState(romname,saveSlot); textClear();return;
|
||||
case MMOPTION_LOADSTATE: sv_loadState(romname,saveSlot); textClear();return;
|
||||
case MMOPTION_SAVESTATE: supervision_save_state(romname,saveSlot); textClear();return;
|
||||
case MMOPTION_LOADSTATE: supervision_load_state(romname,saveSlot); textClear();return;
|
||||
case MMOPTION_EXIT: exitMenu(); break;
|
||||
default: return;
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ void CheckKeys(void)
|
||||
/*if(keys & KEY_START && keys & KEY_SELECT) {
|
||||
dotextmenu();
|
||||
loadROM();
|
||||
supervision_load((uint8*)buffer, (uint32)buffer_size);
|
||||
supervision_load(&buffer, (uint32)buffer_size);
|
||||
iprintf("\nLoad Rom Seccessfully\n"); }*/
|
||||
}
|
||||
|
||||
@ -148,7 +148,7 @@ int main()
|
||||
iprintf("\nFailed to init fat");
|
||||
}
|
||||
|
||||
supervision_load((uint8*)buffer, (uint32)buffer_size);
|
||||
supervision_load(&buffer, (uint32)buffer_size);
|
||||
iprintf("\nLoad Rom Seccessfully\n");
|
||||
|
||||
while(1)
|
||||
|
@ -136,7 +136,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
supervision_init(); //Init the emulator
|
||||
if(romname) loadROM(romname); else loadROM("rom.sv");
|
||||
supervision_load((uint8*)buffer, (uint32)buffer_size);
|
||||
supervision_load(&buffer, (uint32)buffer_size);
|
||||
|
||||
while(1)
|
||||
{
|
||||
|
@ -6,21 +6,36 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
#include "../../common/supervision.h"
|
||||
#include "../../common/sound.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#define OR_DIE(cond) \
|
||||
if (cond) { \
|
||||
fprintf(stderr, "[Error] SDL: %s\n", SDL_GetError()); \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
SDL_bool paused = SDL_FALSE;
|
||||
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
|
||||
|
||||
uint16_t screenBuffer[160 * 160];
|
||||
#define SCREEN_W 160
|
||||
#define SCREEN_H 160
|
||||
|
||||
typedef enum {
|
||||
MENUSTATE_NONE,
|
||||
MENUSTATE_DROP_ROM,
|
||||
MENUSTATE_EMULATION,
|
||||
MENUSTATE_PAUSE,
|
||||
MENUSTATE_SET_KEY
|
||||
} MenuState;
|
||||
|
||||
SDL_bool done = SDL_FALSE;
|
||||
uint16_t screenBuffer[SCREEN_W * SCREEN_H];
|
||||
SDL_Window *sdlScreen;
|
||||
SDL_Renderer *sdlRenderer;
|
||||
SDL_Texture *sdlTexture;
|
||||
@ -28,97 +43,337 @@ SDL_Texture *sdlTexture;
|
||||
uint8_t *buffer;
|
||||
uint32_t bufferSize = 0;
|
||||
|
||||
SDL_GameController *controller = NULL;
|
||||
|
||||
SDL_bool IsFullscreen(void);
|
||||
void ToggleFullscreen(void);
|
||||
uint64_t startCounter = 0;
|
||||
SDL_bool isRefreshRate60 = SDL_FALSE;
|
||||
void InitCounter(void);
|
||||
SDL_bool NeedUpdate(void);
|
||||
void DrawDropROM(void);
|
||||
|
||||
int nextMenuState = 0;
|
||||
MenuState menuStates[4];
|
||||
void PushMenuState(MenuState state);
|
||||
void PopMenuState(void);
|
||||
MenuState GetMenuState(void);
|
||||
|
||||
void Reset(void);
|
||||
|
||||
int currentPalette = 0;
|
||||
void NextPalette(void);
|
||||
|
||||
int windowScale = 4;
|
||||
void IncreaseWindowSize(void);
|
||||
void DecreaseWindowSize(void);
|
||||
|
||||
void SaveState(void);
|
||||
void LoadState(void);
|
||||
|
||||
int audioVolume = SDL_MIX_MAXVOLUME;
|
||||
void SetVolume(int volume);
|
||||
void MuteAudio(void);
|
||||
|
||||
int currentGhosting = 0;
|
||||
void IncreaseGhosting(void);
|
||||
void DecreaseGhosting(void);
|
||||
|
||||
char *keysNames[] = {
|
||||
"Right",
|
||||
"Left",
|
||||
"Down",
|
||||
"Up",
|
||||
"B",
|
||||
"A",
|
||||
"Select",
|
||||
"Start",
|
||||
|
||||
"Toggle Fullscreen",
|
||||
"Reset",
|
||||
"Save State",
|
||||
"Load State",
|
||||
"Next Palette",
|
||||
"Decrease Window Size",
|
||||
"Increase Window Size",
|
||||
"Decrease Ghosting",
|
||||
"Increase Ghosting",
|
||||
"Mute Audio",
|
||||
};
|
||||
int keysMapping[] = {
|
||||
SDL_SCANCODE_RIGHT,
|
||||
SDL_SCANCODE_LEFT,
|
||||
SDL_SCANCODE_DOWN,
|
||||
SDL_SCANCODE_UP,
|
||||
SDL_SCANCODE_X, // B
|
||||
SDL_SCANCODE_C, // A
|
||||
SDL_SCANCODE_Z, // Select
|
||||
SDL_SCANCODE_SPACE, // Start
|
||||
|
||||
SDL_SCANCODE_RETURN,
|
||||
SDL_SCANCODE_TAB,
|
||||
SDL_SCANCODE_1,
|
||||
SDL_SCANCODE_2,
|
||||
SDL_SCANCODE_P,
|
||||
SDL_SCANCODE_MINUS,
|
||||
SDL_SCANCODE_EQUALS,
|
||||
SDL_SCANCODE_LEFTBRACKET,
|
||||
SDL_SCANCODE_RIGHTBRACKET,
|
||||
SDL_SCANCODE_M,
|
||||
};
|
||||
void (*keysFuncs[])(void) = {
|
||||
ToggleFullscreen,
|
||||
Reset,
|
||||
SaveState,
|
||||
LoadState,
|
||||
NextPalette,
|
||||
DecreaseWindowSize,
|
||||
IncreaseWindowSize,
|
||||
DecreaseGhosting,
|
||||
IncreaseGhosting,
|
||||
MuteAudio,
|
||||
};
|
||||
int setButton = -1;
|
||||
void SetKey(int button);
|
||||
|
||||
char romName[64];
|
||||
void SetRomName(const char *path)
|
||||
{
|
||||
const char *p = path + strlen(path);
|
||||
while (p != path) {
|
||||
if (*p == '\\' || *p == '/') {
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
p--;
|
||||
}
|
||||
strncpy(romName, p, sizeof(romName));
|
||||
romName[sizeof(romName) - 1] = '\0';
|
||||
}
|
||||
|
||||
int LoadROM(const char *filename)
|
||||
{
|
||||
if (buffer != 0)
|
||||
if (buffer != NULL) {
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
}
|
||||
|
||||
FILE *romfile = fopen(filename, "rb");
|
||||
SDL_RWops *romfile = SDL_RWFromFile(filename, "rb");
|
||||
if (romfile == NULL) {
|
||||
printf("fopen(): Unable to open file!\n");
|
||||
fprintf(stderr, "SDL_RWFromFile(): Unable to open file!\n");
|
||||
return 1;
|
||||
}
|
||||
fseek(romfile, 0, SEEK_END);
|
||||
bufferSize = ftell(romfile);
|
||||
fseek(romfile, 0, SEEK_SET);
|
||||
|
||||
bufferSize = (uint32_t)SDL_RWsize(romfile);
|
||||
buffer = (uint8_t *)malloc(bufferSize);
|
||||
|
||||
fread(buffer, bufferSize, 1, romfile);
|
||||
|
||||
if (fclose(romfile) == EOF) {
|
||||
printf("fclose(): Unable to close file!\n");
|
||||
SDL_RWread(romfile, buffer, bufferSize, 1);
|
||||
if (SDL_RWclose(romfile) != 0) {
|
||||
fprintf(stderr, "SDL_RWclose(): Unable to close file!\n");
|
||||
return 1;
|
||||
}
|
||||
SetRomName(filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//int LoadROM(const char *filename)
|
||||
//{
|
||||
// if (buffer != NULL) {
|
||||
// free(buffer);
|
||||
// buffer = NULL;
|
||||
// }
|
||||
//
|
||||
// FILE *romfile = fopen(filename, "rb");
|
||||
// if (romfile == NULL) {
|
||||
// printf("fopen(): Unable to open file!\n");
|
||||
// return 1;
|
||||
// }
|
||||
// fseek(romfile, 0, SEEK_END);
|
||||
// bufferSize = ftell(romfile);
|
||||
// fseek(romfile, 0, SEEK_SET);
|
||||
//
|
||||
// buffer = (uint8_t *)malloc(bufferSize);
|
||||
//
|
||||
// fread(buffer, bufferSize, 1, romfile);
|
||||
//
|
||||
// if (fclose(romfile) == EOF) {
|
||||
// printf("fclose(): Unable to close file!\n");
|
||||
// return 1;
|
||||
// }
|
||||
// SetRomName(filename);
|
||||
// return 0;
|
||||
//}
|
||||
|
||||
void LoadBuffer(void)
|
||||
{
|
||||
supervision_load(&buffer, bufferSize);
|
||||
MenuState prevState = GetMenuState();
|
||||
PopMenuState();
|
||||
PushMenuState(MENUSTATE_EMULATION);
|
||||
if (prevState == MENUSTATE_PAUSE) { // Focus wasn't gained
|
||||
PushMenuState(MENUSTATE_PAUSE);
|
||||
}
|
||||
supervision_set_colour_scheme(currentPalette);
|
||||
supervision_set_ghosting(currentGhosting);
|
||||
}
|
||||
|
||||
void AudioCallback(void *userdata, uint8_t *stream, int len)
|
||||
{
|
||||
// The alternative of SDL_PauseAudio()
|
||||
//if (GetMenuState() != MENUSTATE_EMULATION) {
|
||||
// SDL_memset(stream, 0, len);
|
||||
// return;
|
||||
//}
|
||||
|
||||
// U8 to F32
|
||||
sound_stream_update(stream, len / 4);
|
||||
float *s = (float*)(stream + len) - 1;
|
||||
for (int i = len / 4 - 1; i >= 0; i--) {
|
||||
// 127 - max
|
||||
*s-- = stream[i] / 127.0f * audioVolume / (float)SDL_MIX_MAXVOLUME;
|
||||
}
|
||||
|
||||
// U8 or S8
|
||||
/*sound_stream_update(stream, len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
stream[i] = (uint8_t)(stream[i] * audioVolume / (float)SDL_MIX_MAXVOLUME);
|
||||
}*/
|
||||
}
|
||||
|
||||
void HandleInput(void)
|
||||
{
|
||||
uint8_t controls_state = 0;
|
||||
const uint8_t *keystate = SDL_GetKeyboardState(NULL);
|
||||
|
||||
if (keystate[SDL_SCANCODE_RIGHT]) controls_state |= 0x01;
|
||||
if (keystate[SDL_SCANCODE_LEFT]) controls_state |= 0x02;
|
||||
if (keystate[SDL_SCANCODE_DOWN]) controls_state |= 0x04;
|
||||
if (keystate[SDL_SCANCODE_UP]) controls_state |= 0x08;
|
||||
if (keystate[SDL_SCANCODE_X]) controls_state |= 0x10; // B
|
||||
if (keystate[SDL_SCANCODE_C]) controls_state |= 0x20; // A
|
||||
if (keystate[SDL_SCANCODE_Z]) controls_state |= 0x40; // Select
|
||||
if (keystate[SDL_SCANCODE_SPACE]) controls_state |= 0x80; // Start
|
||||
if (keystate[keysMapping[0]]) controls_state |= 0x01;
|
||||
if (keystate[keysMapping[1]]) controls_state |= 0x02;
|
||||
if (keystate[keysMapping[2]]) controls_state |= 0x04;
|
||||
if (keystate[keysMapping[3]]) controls_state |= 0x08;
|
||||
if (keystate[keysMapping[4]]) controls_state |= 0x10; // B
|
||||
if (keystate[keysMapping[5]]) controls_state |= 0x20; // A
|
||||
if (keystate[keysMapping[6]]) controls_state |= 0x40; // Select
|
||||
if (keystate[keysMapping[7]]) controls_state |= 0x80; // Start
|
||||
|
||||
if (SDL_GameControllerGetAttached(controller)) {
|
||||
if (SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) controls_state |= 0x01;
|
||||
if (SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT)) controls_state |= 0x02;
|
||||
if (SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN)) controls_state |= 0x04;
|
||||
if (SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_UP)) controls_state |= 0x08;
|
||||
if (SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_A)) controls_state |= 0x10;
|
||||
if (SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_X)) controls_state |= 0x20;
|
||||
if (SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_BACK)) controls_state |= 0x40;
|
||||
if (SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_START)) controls_state |= 0x80;
|
||||
// 31130/32768 == 0.95
|
||||
if (SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) > 31130) controls_state |= 0x01;
|
||||
if (SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) < -31130) controls_state |= 0x02;
|
||||
if (SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) > 31130) controls_state |= 0x04;
|
||||
if (SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) < -31130) controls_state |= 0x08;
|
||||
}
|
||||
|
||||
controls_state_write(0, controls_state);
|
||||
}
|
||||
|
||||
void AudioCallback(void *userdata, uint8_t *stream, int len)
|
||||
{
|
||||
//SDL_memset(stream, 0, len);
|
||||
sound_stream_update(stream, len/4);
|
||||
// U8 to F32
|
||||
int i;
|
||||
float *s = (float*)(stream + len) - 1;
|
||||
for (i = len/4 - 1; i >= 0; i--) {
|
||||
*s-- = stream[i] / 255.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void Render(void)
|
||||
{
|
||||
SDL_RenderClear(sdlRenderer);
|
||||
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
|
||||
SDL_RenderPresent(sdlRenderer);
|
||||
}
|
||||
|
||||
void PollEvents(void)
|
||||
{
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (event.type == SDL_QUIT) {
|
||||
paused = SDL_TRUE;
|
||||
done = SDL_TRUE;
|
||||
}
|
||||
else if (event.type == SDL_KEYDOWN) {
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_RETURN:
|
||||
ToggleFullscreen();
|
||||
break;
|
||||
case SDLK_1:
|
||||
sv_saveState("rom ", 0);
|
||||
break;
|
||||
case SDLK_2:
|
||||
sv_loadState("rom ", 0);
|
||||
if (GetMenuState() == MENUSTATE_SET_KEY) {
|
||||
if (event.key.keysym.scancode != SDL_SCANCODE_ESCAPE)
|
||||
keysMapping[setButton] = event.key.keysym.scancode;
|
||||
PopMenuState();
|
||||
SDL_PauseAudio(0);
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < COUNT_OF(keysFuncs); i++) {
|
||||
if (keysMapping[i + 8] == event.key.keysym.scancode) {
|
||||
keysFuncs[i]();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
if (!IsFullscreen())
|
||||
SDL_SetWindowSize(sdlScreen, (event.window.data1 + 80) / 160 * 160, (event.window.data2 + 80) / 160 * 160);
|
||||
// SDL_CONTROLLERBUTTONDOWN doesn't work
|
||||
else if (event.type == SDL_JOYBUTTONDOWN) {
|
||||
if (SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) {
|
||||
SaveState();
|
||||
}
|
||||
else if (SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) {
|
||||
LoadState();
|
||||
}
|
||||
}
|
||||
else if (event.type == SDL_WINDOWEVENT) {
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
if (!IsFullscreen()) {
|
||||
SDL_SetWindowSize(sdlScreen,
|
||||
(event.window.data1 + SCREEN_W / 2) / SCREEN_W * SCREEN_W,
|
||||
(event.window.data2 + SCREEN_H / 2) / SCREEN_H * SCREEN_H);
|
||||
}
|
||||
break;
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
if (GetMenuState() == MENUSTATE_SET_KEY) PopMenuState();
|
||||
PushMenuState(MENUSTATE_PAUSE);
|
||||
SDL_PauseAudio(1);
|
||||
break;
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
if (GetMenuState() == MENUSTATE_PAUSE) PopMenuState();
|
||||
SDL_PauseAudio(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (event.type == SDL_DROPFILE) {
|
||||
if (LoadROM(event.drop.file) == 0) {
|
||||
LoadBuffer();
|
||||
}
|
||||
SDL_free(event.drop.file);
|
||||
}
|
||||
else if (event.type == SDL_JOYDEVICEADDED) {
|
||||
if (SDL_IsGameController(event.jdevice.which)) {
|
||||
controller = SDL_GameControllerOpen(event.jdevice.which);
|
||||
if (!controller) {
|
||||
fprintf(stderr, "Could not open gamecontroller %i: %s\n", event.jdevice.which, SDL_GetError());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Loop(void)
|
||||
{
|
||||
PollEvents();
|
||||
|
||||
HandleInput();
|
||||
|
||||
while (NeedUpdate()) {
|
||||
switch (GetMenuState()) {
|
||||
case MENUSTATE_EMULATION:
|
||||
supervision_exec(screenBuffer);
|
||||
break;
|
||||
case MENUSTATE_DROP_ROM:
|
||||
DrawDropROM();
|
||||
break;
|
||||
case MENUSTATE_PAUSE:
|
||||
case MENUSTATE_SET_KEY:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw
|
||||
SDL_UpdateTexture(sdlTexture, NULL, screenBuffer, SCREEN_W * sizeof(uint16_t));
|
||||
|
||||
SDL_RenderClear(sdlRenderer);
|
||||
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
|
||||
SDL_RenderPresent(sdlRenderer);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if (done) {
|
||||
emscripten_cancel_main_loop();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
OR_DIE(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0);
|
||||
@ -126,64 +381,62 @@ int main(int argc, char *argv[])
|
||||
sdlScreen = SDL_CreateWindow("Potator (SDL2)",
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
160*3, 160*3,
|
||||
SCREEN_W * windowScale, SCREEN_H * windowScale,
|
||||
SDL_WINDOW_RESIZABLE);
|
||||
OR_DIE(sdlScreen == NULL);
|
||||
|
||||
|
||||
sdlRenderer = SDL_CreateRenderer(sdlScreen, -1, SDL_RENDERER_PRESENTVSYNC);
|
||||
OR_DIE(sdlRenderer == NULL);
|
||||
|
||||
SDL_RenderSetLogicalSize(sdlRenderer, 160, 160);
|
||||
|
||||
|
||||
SDL_RenderSetLogicalSize(sdlRenderer, SCREEN_W, SCREEN_H);
|
||||
|
||||
sdlTexture = SDL_CreateTexture(sdlRenderer,
|
||||
SDL_PIXELFORMAT_RGB555,
|
||||
SDL_PIXELFORMAT_BGR555,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
160, 160);
|
||||
SCREEN_W, SCREEN_H);
|
||||
OR_DIE(sdlTexture == NULL);
|
||||
|
||||
SDL_AudioSpec audio_spec;
|
||||
SDL_memset(&audio_spec, 0, sizeof(SDL_AudioSpec));
|
||||
SDL_zero(audio_spec);
|
||||
audio_spec.freq = BPS;
|
||||
audio_spec.channels = 2;
|
||||
audio_spec.samples = 512;
|
||||
audio_spec.format = AUDIO_F32; // Problem with U8
|
||||
audio_spec.format = AUDIO_F32; // Or AUDIO_S8. Problem with AUDIO_U8
|
||||
audio_spec.callback = AudioCallback;
|
||||
audio_spec.userdata = NULL;
|
||||
//SDL_OpenAudio(&audio_spec, NULL);
|
||||
SDL_AudioDeviceID devid = SDL_OpenAudioDevice(NULL, 0, &audio_spec, NULL, 0);
|
||||
OR_DIE(devid == 0);
|
||||
OR_DIE(SDL_OpenAudio(&audio_spec, NULL) == -1);
|
||||
//SDL_AudioDeviceID devid = SDL_OpenAudioDevice(NULL, 0, &audio_spec, NULL, 0);
|
||||
//OR_DIE(devid == 0);
|
||||
|
||||
if (argc <= 1) {
|
||||
LoadROM("rom.sv");
|
||||
}
|
||||
else {
|
||||
LoadROM(argv[1]);
|
||||
printf("# Controls\n");
|
||||
for (int i = 0; i < COUNT_OF(keysNames); i++) {
|
||||
printf("%20s: %s\n", keysNames[i], SDL_GetScancodeName(keysMapping[i]));
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
supervision_init();
|
||||
supervision_load(buffer, bufferSize);
|
||||
|
||||
//SDL_PauseAudio(0);
|
||||
SDL_PauseAudioDevice(devid, 0);
|
||||
|
||||
while (!paused) {
|
||||
PollEvents();
|
||||
|
||||
HandleInput();
|
||||
|
||||
// Emulate
|
||||
supervision_exec(screenBuffer);
|
||||
|
||||
// Draw
|
||||
SDL_UpdateTexture(sdlTexture, NULL, screenBuffer, 160 * sizeof(uint16_t));
|
||||
|
||||
Render();
|
||||
PushMenuState(MENUSTATE_DROP_ROM);
|
||||
if (LoadROM(argc <= 1 ? "rom.sv" : argv[1]) == 0) {
|
||||
LoadBuffer();
|
||||
}
|
||||
|
||||
SDL_PauseAudio(0);
|
||||
//SDL_PauseAudioDevice(devid, 0);
|
||||
|
||||
InitCounter();
|
||||
#ifdef __EMSCRIPTEN__
|
||||
emscripten_set_main_loop(Loop, 0, 1);
|
||||
#else
|
||||
while (!done) {
|
||||
Loop();
|
||||
}
|
||||
#endif
|
||||
|
||||
supervision_done();
|
||||
|
||||
//SDL_CloseAudio();
|
||||
SDL_CloseAudioDevice(devid);
|
||||
SDL_CloseAudio();
|
||||
//SDL_CloseAudioDevice(devid);
|
||||
|
||||
SDL_DestroyTexture(sdlTexture);
|
||||
SDL_DestroyRenderer(sdlRenderer);
|
||||
@ -193,9 +446,11 @@ int main(int argc, char *argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FULLSCREEN_FLAG SDL_WINDOW_FULLSCREEN_DESKTOP
|
||||
|
||||
SDL_bool IsFullscreen(void)
|
||||
{
|
||||
return 0 != (SDL_GetWindowFlags(sdlScreen) & SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
return 0 != (SDL_GetWindowFlags(sdlScreen) & FULLSCREEN_FLAG);
|
||||
}
|
||||
|
||||
void ToggleFullscreen(void)
|
||||
@ -203,9 +458,11 @@ void ToggleFullscreen(void)
|
||||
static int mouseX;
|
||||
static int mouseY;
|
||||
static SDL_bool cursorInWindow = SDL_FALSE;
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
return;
|
||||
#endif
|
||||
if (!IsFullscreen()) {
|
||||
SDL_SetWindowFullscreen(sdlScreen, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
SDL_SetWindowFullscreen(sdlScreen, FULLSCREEN_FLAG);
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
|
||||
int x, y;
|
||||
@ -219,7 +476,7 @@ void ToggleFullscreen(void)
|
||||
SDL_SetWindowFullscreen(sdlScreen, 0);
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
|
||||
// Don't move cursor
|
||||
// Don't move cursor. Bug?
|
||||
if (cursorInWindow) {
|
||||
SDL_WarpMouseInWindow(sdlScreen, mouseX, mouseY);
|
||||
}
|
||||
@ -229,3 +486,187 @@ void ToggleFullscreen(void)
|
||||
cursorInWindow = SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void InitCounter(void)
|
||||
{
|
||||
SDL_DisplayMode current;
|
||||
SDL_GetCurrentDisplayMode(0, ¤t);
|
||||
isRefreshRate60 = (current.refresh_rate == 60);
|
||||
startCounter = SDL_GetPerformanceCounter();
|
||||
}
|
||||
|
||||
SDL_bool NeedUpdate(void)
|
||||
{
|
||||
static double elapsedCounter = 0.0;
|
||||
static SDL_bool result = SDL_FALSE;
|
||||
|
||||
if (isRefreshRate60) {
|
||||
result = !result;
|
||||
}
|
||||
else {
|
||||
// New frame
|
||||
if (!result) {
|
||||
uint64_t now = SDL_GetPerformanceCounter();
|
||||
elapsedCounter += (double)((now - startCounter) * 1000) / SDL_GetPerformanceFrequency();
|
||||
startCounter = now;
|
||||
}
|
||||
result = elapsedCounter >= 16.666;
|
||||
if (result) {
|
||||
elapsedCounter -= 16.666;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void DrawDropROM(void)
|
||||
{
|
||||
static uint8_t fade = 0;
|
||||
char dropRom[] = {
|
||||
"## ## ### ## ## ### # #"
|
||||
"# # # # # # # # # # # # ###"
|
||||
"# # ## # # ## ## # # # #"
|
||||
"## # # ### # # # ### # #"
|
||||
};
|
||||
uint8_t f = (fade < 32) ? fade : 63 - fade;
|
||||
uint16_t color = (f << 0) | (f << 5) | (f << 10);
|
||||
fade = (fade + 1) % 64;
|
||||
|
||||
int width = 28, height = 4;
|
||||
int scale = 4, start = (SCREEN_W - width * scale) / 2 + SCREEN_W * (SCREEN_H - height * scale) / 2;
|
||||
for (int j = 0; j < height * scale; j++) {
|
||||
for (int i = 0; i < width * scale; i++) {
|
||||
if (dropRom[i / scale + width * (j / scale)] == '#')
|
||||
screenBuffer[start + i + SCREEN_W * j] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PushMenuState(MenuState state)
|
||||
{
|
||||
if (nextMenuState == 0 || (nextMenuState > 0 && menuStates[nextMenuState - 1] != state)) {
|
||||
menuStates[nextMenuState] = state;
|
||||
nextMenuState++;
|
||||
}
|
||||
}
|
||||
|
||||
void PopMenuState(void)
|
||||
{
|
||||
if (nextMenuState > 0)
|
||||
nextMenuState--;
|
||||
}
|
||||
|
||||
MenuState GetMenuState(void)
|
||||
{
|
||||
if (nextMenuState > 0)
|
||||
return menuStates[nextMenuState - 1];
|
||||
return MENUSTATE_NONE;
|
||||
}
|
||||
|
||||
// Emscripten
|
||||
void UploadROM(void *newBuffer, int newBufferSize, const char *fileName)
|
||||
{
|
||||
bufferSize = newBufferSize;
|
||||
if (buffer != NULL) {
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
}
|
||||
buffer = (uint8_t *)malloc(bufferSize);
|
||||
memcpy(buffer, newBuffer, bufferSize);
|
||||
|
||||
SetRomName(fileName);
|
||||
LoadBuffer();
|
||||
}
|
||||
|
||||
void Reset(void)
|
||||
{
|
||||
if (GetMenuState() == MENUSTATE_EMULATION)
|
||||
LoadBuffer();
|
||||
}
|
||||
|
||||
void NextPalette(void)
|
||||
{
|
||||
currentPalette = (currentPalette + 1) % COLOUR_SCHEME_COUNT;
|
||||
supervision_set_colour_scheme(currentPalette);
|
||||
}
|
||||
|
||||
void IncreaseWindowSize(void)
|
||||
{
|
||||
if (IsFullscreen())
|
||||
return;
|
||||
|
||||
SDL_DisplayMode dm;
|
||||
SDL_GetDesktopDisplayMode(0, &dm);
|
||||
if (SCREEN_W * (windowScale + 1) <= dm.w && SCREEN_H * (windowScale + 1) <= dm.h) {
|
||||
windowScale++;
|
||||
SDL_SetWindowSize(sdlScreen, SCREEN_W * windowScale, SCREEN_H * windowScale);
|
||||
}
|
||||
}
|
||||
|
||||
void DecreaseWindowSize(void)
|
||||
{
|
||||
if (IsFullscreen())
|
||||
return;
|
||||
|
||||
if (windowScale > 1) {
|
||||
windowScale--;
|
||||
SDL_SetWindowSize(sdlScreen, SCREEN_W * windowScale, SCREEN_H * windowScale);
|
||||
}
|
||||
}
|
||||
|
||||
void SaveState(void)
|
||||
{
|
||||
if (GetMenuState() == MENUSTATE_EMULATION)
|
||||
supervision_save_state(romName, 0);
|
||||
}
|
||||
|
||||
void LoadState(void)
|
||||
{
|
||||
if (GetMenuState() == MENUSTATE_EMULATION)
|
||||
supervision_load_state(romName, 0);
|
||||
}
|
||||
|
||||
void SetVolume(int volume)
|
||||
{
|
||||
if (volume < 0)
|
||||
audioVolume = 0;
|
||||
else if (volume > SDL_MIX_MAXVOLUME)
|
||||
audioVolume = SDL_MIX_MAXVOLUME; // 128
|
||||
else
|
||||
audioVolume = volume;
|
||||
}
|
||||
|
||||
void MuteAudio(void)
|
||||
{
|
||||
static int lastVolume = -1;
|
||||
if (lastVolume == -1) {
|
||||
lastVolume = audioVolume;
|
||||
audioVolume = 0;
|
||||
}
|
||||
else {
|
||||
audioVolume = lastVolume;
|
||||
lastVolume = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void IncreaseGhosting(void)
|
||||
{
|
||||
if (currentGhosting < GHOSTING_MAX) {
|
||||
currentGhosting++;
|
||||
supervision_set_ghosting(currentGhosting);
|
||||
}
|
||||
}
|
||||
|
||||
void DecreaseGhosting(void)
|
||||
{
|
||||
if (currentGhosting > 0) {
|
||||
currentGhosting--;
|
||||
supervision_set_ghosting(currentGhosting);
|
||||
}
|
||||
}
|
||||
|
||||
void SetKey(int button)
|
||||
{
|
||||
setButton = button;
|
||||
PushMenuState(MENUSTATE_SET_KEY);
|
||||
SDL_PauseAudio(1);
|
||||
}
|
||||
|
10
platform/SDL2/sdl2-config.cmake
Normal file
10
platform/SDL2/sdl2-config.cmake
Normal file
@ -0,0 +1,10 @@
|
||||
set(SDL2_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/include")
|
||||
|
||||
# Support both 32 and 64 bit builds
|
||||
if (${CMAKE_SIZEOF_VOID_P} EQUAL 8)
|
||||
set(SDL2_LIBRARY "${CMAKE_CURRENT_LIST_DIR}/lib/x64/SDL2.lib;${CMAKE_CURRENT_LIST_DIR}/lib/x64/SDL2main.lib")
|
||||
else ()
|
||||
set(SDL2_LIBRARY "${CMAKE_CURRENT_LIST_DIR}/lib/x86/SDL2.lib;${CMAKE_CURRENT_LIST_DIR}/lib/x86/SDL2main.lib")
|
||||
endif ()
|
||||
|
||||
string(STRIP "${SDL2_LIBRARY}" SDL2_LIBRARY)
|
@ -260,7 +260,7 @@ LRESULT CALLBACK WndProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
}
|
||||
|
||||
loadROM(filename);
|
||||
supervision_load((UINT8*)buffer, (UINT32)buffer_size);
|
||||
supervision_load(&buffer, (UINT32)buffer_size);
|
||||
execute=TRUE;
|
||||
}
|
||||
break;
|
||||
|
49
platform/emscripten/Makefile
Normal file
49
platform/emscripten/Makefile
Normal file
@ -0,0 +1,49 @@
|
||||
# Emscripten makefile
|
||||
# Based on https://github.com/aardappel/lobster/blob/master/dev/emscripten/Makefile
|
||||
|
||||
CC = emcc
|
||||
|
||||
# use -g4 for maximum debug info.
|
||||
OPTLEVEL = -g4 -O0
|
||||
CFLAGS = $(OPTLEVEL)
|
||||
CFLAGS += -Wall -s USE_SDL=2
|
||||
|
||||
CSRCS = \
|
||||
../SDL2/main.c \
|
||||
../../common/m6502/m6502.c \
|
||||
$(wildcard ../../common/*.c)
|
||||
|
||||
COBJS := $(patsubst %.c,%.o,$(CSRCS))
|
||||
|
||||
EXPORTED_FUNCTIONS = \
|
||||
'_main', \
|
||||
'_UploadROM', \
|
||||
'_SaveState', \
|
||||
'_LoadState', \
|
||||
'_NextPalette', \
|
||||
'_IncreaseWindowSize', \
|
||||
'_DecreaseWindowSize', \
|
||||
'_SetVolume', \
|
||||
'_SetKey', \
|
||||
'_MuteAudio', \
|
||||
'_supervision_set_ghosting'
|
||||
|
||||
.PHONY: potator clean clean2 release all default
|
||||
|
||||
# add -s ASSERTIONS=2 when troubleshooting.
|
||||
#EMCC_WASM_BACKEND=1
|
||||
potator: $(COBJS)
|
||||
$(CC) $(CFLAGS) $(COBJS) \
|
||||
-s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']" \
|
||||
-s "EXPORTED_FUNCTIONS=[$(EXPORTED_FUNCTIONS)]" \
|
||||
-o index.html --shell-file potator_shell.html
|
||||
|
||||
clean clean2:
|
||||
-$(RM) $(COBJS)
|
||||
|
||||
release: OPTLEVEL = -O2
|
||||
release: clean potator clean2
|
||||
|
||||
all: potator
|
||||
|
||||
default: all
|
872
platform/emscripten/potator_shell.html
Normal file
872
platform/emscripten/potator_shell.html
Normal file
@ -0,0 +1,872 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Emscripten-Generated Code</title>
|
||||
<style>
|
||||
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
|
||||
textarea.emscripten { font-family: monospace; width: 80%; }
|
||||
div.emscripten { text-align: center; }
|
||||
div.emscripten_border { /*border: 1px solid black;*/ }
|
||||
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
|
||||
canvas.emscripten { border: 0px none; background-color: black; }
|
||||
|
||||
.spinner {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
margin: 0px auto;
|
||||
-webkit-animation: rotation .8s linear infinite;
|
||||
-moz-animation: rotation .8s linear infinite;
|
||||
-o-animation: rotation .8s linear infinite;
|
||||
animation: rotation 0.8s linear infinite;
|
||||
border-left: 10px solid rgb(0,150,240);
|
||||
border-right: 10px solid rgb(0,150,240);
|
||||
border-bottom: 10px solid rgb(0,150,240);
|
||||
border-top: 10px solid rgb(100,0,200);
|
||||
border-radius: 100%;
|
||||
background-color: rgb(200,100,250);
|
||||
}
|
||||
@-webkit-keyframes rotation {
|
||||
from {-webkit-transform: rotate(0deg);}
|
||||
to {-webkit-transform: rotate(360deg);}
|
||||
}
|
||||
@-moz-keyframes rotation {
|
||||
from {-moz-transform: rotate(0deg);}
|
||||
to {-moz-transform: rotate(360deg);}
|
||||
}
|
||||
@-o-keyframes rotation {
|
||||
from {-o-transform: rotate(0deg);}
|
||||
to {-o-transform: rotate(360deg);}
|
||||
}
|
||||
@keyframes rotation {
|
||||
from {transform: rotate(0deg);}
|
||||
to {transform: rotate(360deg);}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
line-height: 1.1;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.main-button, canvas {
|
||||
image-rendering: pixelated;
|
||||
image-rendering: crisp-edges;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
image-rendering: -webkit-crisp-edges;
|
||||
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.main-button {
|
||||
width: 48px;
|
||||
height: 36px;
|
||||
box-sizing: content-box;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-origin: content-box;
|
||||
border: 2px solid #aaa;
|
||||
margin: 1px;
|
||||
padding: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.main-button:hover {
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
#fileb {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAMAAABcOc2zAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAB5QTFRFvALakJCQ2gIFAjraHtoC+MBI8IgAgq7I+Kgw////JqDhkgAAAAp0Uk5T////////////ALLMLM8AAABJSURBVHjaXI5BDoAwDMPC2s3t/z+MADFafLSiJJrRmFqzsbSCQlxijOHuhkl6BDhuZnoT7MQnuEXpoHb8VujHUHaRSo4CeQowALRTBDws+boxAAAAAElFTkSuQmCC');
|
||||
}
|
||||
|
||||
#savestateb {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAMAAABcOc2zAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAxQTFRFZmZmmZmZAAAA////ugmYcAAAAAR0Uk5T////AEAqqfQAAAA4SURBVHjajIvRCgAgCAO38///OVCLCB+ag9PhFCXoRZ8Bqfvj3EPFUdOweCT0GLOdULMCD5UlwADBvAGarVk1DAAAAABJRU5ErkJggg==');
|
||||
}
|
||||
|
||||
#loadstateb {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAMAAABcOc2zAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAxQTFRFZmZmmZmZAAAA////ugmYcAAAAAR0Uk5T////AEAqqfQAAAA5SURBVHjafIvRCgAwCAIv+/9/HizbQ2wzwTyQLEl+6N6E0014TH4gss4RaAgxrFB7B84CcZksAQYAwRIBmu1bY5MAAAAASUVORK5CYII=');
|
||||
}
|
||||
|
||||
#muteb {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAMAAABcOc2zAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAlQTFRFZmZmAAAA////UkbnHwAAAEJJREFUeNp0zgEKACAIA8DN/z86l8swaFDBsUrEE/jk2Q2kl4FkFQSsqDAhvoCMADDsz+tRNKSEKrowBsSdtLMEGAB8ZgExwXIzcQAAAABJRU5ErkJggg==');
|
||||
}
|
||||
#muteab {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAMAAABcOc2zAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAlQTFRFZmZmAAAA////UkbnHwAAADBJREFUeNpiYEIDDFCaEU2AEU2AEVmAEQJIEWAAAkZ0QxnQBYAi6A5jQBeAA4AAAwCHogFSzpCvUAAAAABJRU5ErkJggg==')
|
||||
}
|
||||
|
||||
#decreaseb {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAMAAABcOc2zAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRFAAAAAAAApWe5zwAAAAJ0Uk5T/wDltzBKAAAAJElEQVR42mJgwACMMABmUUkA1XhkFYwYAiAOfhUYhoIBQIABADFuAHvO4HrHAAAAAElFTkSuQmCC');
|
||||
}
|
||||
|
||||
#increaseb {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAMAAABcOc2zAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRFAAAAAAAApWe5zwAAAAJ0Uk5T/wDltzBKAAAAJUlEQVR42mJgwACMMABmUUkA1XhkFWA1KAIgDn4VGIaCAUCAAQAxAAB51Z56IgAAAABJRU5ErkJggg==');
|
||||
}
|
||||
|
||||
#nextpaletteb {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAMAAABcOc2zAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABhQTFRFJEc7uc2rb6Bag71qMWJSABAY3vbNAAAAjQpJGgAAACdJREFUeNpiYIQCJihgYGSDAGYIoI4AAwsEsEIAO3UEoIAdCgACDAAfcgL57TF56gAAAABJRU5ErkJggg==');
|
||||
}
|
||||
|
||||
#fullscreenb {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAMAAABcOc2zAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRFAAAAAAAApWe5zwAAAAJ0Uk5T/wDltzBKAAAAIUlEQVR42mJggABGRgYGOBMZkCXAiAYwBahhC7rTAQIMADvEAJWS8qCXAAAAAElFTkSuQmCC');
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: inline-block;
|
||||
padding-bottom: 9px;
|
||||
margin: 40px 0 20px;
|
||||
border-bottom: 2px solid #eee;
|
||||
}
|
||||
|
||||
/* https://www.w3schools.com/cSS/css_tooltip.asp */
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 120px;
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 110%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: #444 transparent transparent transparent;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
transition: visibility 0s linear 0.5s; /* https://stackoverflow.com/q/36242258 */
|
||||
}
|
||||
|
||||
.tooltip .tooltipaudio {
|
||||
visibility: hidden;
|
||||
width: 120px;
|
||||
background-color: #4449;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 6px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: rotate(-90deg) translateX(-21px);
|
||||
transform-origin: 0px 20px;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltipaudio {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.tooltipinfo {
|
||||
border: 1px solid black;
|
||||
border-radius: 100%;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
text-align: center;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
/* https://www.w3schools.com/howto/howto_js_rangeslider.asp */
|
||||
.slider {
|
||||
-webkit-appearance: none;
|
||||
width: 100%;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
background: #d3d3d3;
|
||||
outline: none;
|
||||
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
/*box-sizing: content-box;*/
|
||||
|
||||
background-color: white;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 0px solid #888;
|
||||
border-radius: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb {
|
||||
background-color: white;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 0px solid #888;
|
||||
border-radius: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
.slider::-moz-range-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#ghostslider::-webkit-slider-thumb {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAxQTFRF////AAAAnJyc////cfZmagAAAAR0Uk5T////AEAqqfQAAABzSURBVHjanJFREgARDEOTuv+dt2ip4sPmo0PeKBWUizBWbNqAeqKaCO6LyQmSPwiyrySA1lys9iMGSNCrDFAtAF4DkD/gfIcsr+rPwjaGTeJzvAJeQDrCK1i+nSGSGJTuOmUCtW+lxYOfmRstMcGTPgEGAKVGBNcv/z1ZAAAAAElFTkSuQmCC');
|
||||
}
|
||||
|
||||
#ghostslider::-moz-range-thumb {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAxQTFRF////AAAAnJyc////cfZmagAAAAR0Uk5T////AEAqqfQAAABzSURBVHjanJFREgARDEOTuv+dt2ip4sPmo0PeKBWUizBWbNqAeqKaCO6LyQmSPwiyrySA1lys9iMGSNCrDFAtAF4DkD/gfIcsr+rPwjaGTeJzvAJeQDrCK1i+nSGSGJTuOmUCtW+lxYOfmRstMcGTPgEGAKVGBNcv/z1ZAAAAAElFTkSuQmCC');
|
||||
}
|
||||
|
||||
#ghost {
|
||||
box-sizing: border-box;
|
||||
width: 158px;
|
||||
height: 46px;
|
||||
padding: 8px 4px;
|
||||
margin: 1px;
|
||||
border: 2px solid #aaa;
|
||||
}
|
||||
#ghost:hover {
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
/* autohotkey.com */
|
||||
kbd {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 0 rgba(0,0,0,.2),0 0 0 2px #fff inset;
|
||||
color: #333;
|
||||
display: inline-block;
|
||||
font-family: Consolas,Courier New,monospace;
|
||||
line-height: 1.4em;
|
||||
margin: 0 .1em;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
padding: 0 .5em;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
white-space: nowrap;
|
||||
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td, th {
|
||||
border: 1px solid #dddddd;
|
||||
text-align: left;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
.ps-bg {
|
||||
display: inline-block;
|
||||
background-color: #484848;
|
||||
border-radius: 100%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.ps-square {
|
||||
display: inline-block;
|
||||
border: solid 2px #e398c9;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.ps-cross {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) rotate(45deg);
|
||||
/* https://stackoverflow.com/a/17359874 */
|
||||
background: linear-gradient(to bottom, transparent 44%,
|
||||
#a2abd6 44%,
|
||||
#a2abd6 56%,
|
||||
transparent 56%),
|
||||
linear-gradient(to right, transparent 44%,
|
||||
#a2abd6 44%,
|
||||
#a2abd6 56%,
|
||||
transparent 56%);
|
||||
}
|
||||
.ps-select {
|
||||
display: inline-block;
|
||||
background-color: #484848;
|
||||
border-radius: 2px;
|
||||
width: 20px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.ps-start {
|
||||
display: inline-block;
|
||||
border-radius: 2px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 6px solid transparent;
|
||||
border-left: 20px solid #484848;
|
||||
border-bottom: 6px solid transparent;
|
||||
}
|
||||
|
||||
.ps-up, .ps-right, .ps-down, .ps-left {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #484848;
|
||||
position: relative;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.ps-right {
|
||||
transform: rotate(90deg) translateY(-2px);
|
||||
margin: 0 1px 0 2px;
|
||||
}
|
||||
.ps-down {
|
||||
transform: rotate(180deg) translateY(-2px);
|
||||
}
|
||||
.ps-left {
|
||||
transform: rotate(-90deg) translateY(-2px);
|
||||
margin: 0 2px 0 1px;
|
||||
}
|
||||
.ps-up::after, .ps-right::after, .ps-down::after, .ps-left::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -50%;
|
||||
border-width: 6px;
|
||||
border-style: solid;
|
||||
border-color: #484848 transparent transparent transparent;
|
||||
}
|
||||
|
||||
#dropzone {
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
z-index: 666;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgb(0, 128, 192);
|
||||
opacity: 0.5;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.preview-td {
|
||||
background-color: #111;
|
||||
padding: 0px;
|
||||
width: 0px;
|
||||
text-align: center;
|
||||
}
|
||||
.preview-img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.preview-img:hover {
|
||||
transform: scale(2) translateX(-20px);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="text-align: center;">
|
||||
<h1 class="page-header">Potator - Watara Supervision Emulator</h1>
|
||||
</div>
|
||||
<div class="emscripten_border" style="position: relative; background: #111;" ondblclick="toggleFullscreen()">
|
||||
<figure style="position: absolute; top: 50%; left: 50%; margin: 0; transform: translate(-50%, -50%);" id="spinner"><div class="spinner"></div></figure>
|
||||
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" width="640" height="640"></canvas>
|
||||
</div>
|
||||
<div class="emscripten" style="text-align: left; width: 640px; margin: 3px auto;">
|
||||
<div class="tooltip" style="vertical-align: top;">
|
||||
<label>
|
||||
<div id="fileb" class="main-button"></div>
|
||||
<input type="file" onchange="onChange(event)" accept=".bin, .sv, .ws" style="display: none;">
|
||||
</label>
|
||||
<span class="tooltiptext">Load ROM</span>
|
||||
</div><!-- delete space
|
||||
--><div class="tooltip" style="vertical-align: top;">
|
||||
<button id="savestateb" class="main-button" onclick="Module.ccall('SaveState', 'void', ['void'])"></button>
|
||||
<span class="tooltiptext">Save state <kbd>1</kbd></span>
|
||||
</div><!-- delete space
|
||||
--><div class="tooltip" style="vertical-align: top;">
|
||||
<button id="loadstateb" class="main-button" onclick="Module.ccall('LoadState', 'void', ['void'])"></button>
|
||||
<span class="tooltiptext">Load state <kbd>2</kbd></span>
|
||||
</div><!-- delete space
|
||||
--><div class="tooltip" id="ghost">
|
||||
<input id="ghostslider" class="slider" type="range" min="0" max="8" value="0" step="1">
|
||||
<span class="tooltiptext" style="width: 200px;">Ghosting (reduce flickering)</span>
|
||||
</div>
|
||||
<div style="float: right;">
|
||||
<div class="tooltip">
|
||||
<button id="muteb" class="main-button" onclick="this.id = (this.id=='muteb'?'muteab':'muteb'); Module.ccall('MuteAudio', 'void', ['void'])"></button>
|
||||
<span class="tooltipaudio">
|
||||
<input id="volumeslider" class="slider" type="range" min="0" max="128" value="128" step="1">
|
||||
</span>
|
||||
</div><!-- delete space
|
||||
--><div class="tooltip">
|
||||
<button id="nextpaletteb" class="main-button" onclick="Module.ccall('NextPalette', 'void', ['void'])"></button>
|
||||
<span class="tooltiptext">Next palette <kbd>P</kbd></span>
|
||||
</div><!-- delete space
|
||||
--><div class="tooltip">
|
||||
<button id="decreaseb" class="main-button" onclick="Module.ccall('DecreaseWindowSize', 'void', ['void'])"></button>
|
||||
<span class="tooltiptext">Decrease window size <kbd>-_</kbd></span>
|
||||
</div><!-- delete space
|
||||
--><div class="tooltip">
|
||||
<button id="increaseb" class="main-button" onclick="Module.ccall('IncreaseWindowSize', 'void', ['void'])"></button>
|
||||
<span class="tooltiptext">Increase window size <kbd>=+</kbd></span>
|
||||
</div><!-- delete space
|
||||
--><div class="tooltip">
|
||||
<button id="fullscreenb" class="main-button" onclick="Module.requestFullscreen(true, false)"></button>
|
||||
<span class="tooltiptext">Fullscreen</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Controls</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Action</th>
|
||||
<th>Keyboard</th>
|
||||
<th>Gamepad</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Right</td>
|
||||
<td><span class="keyName">Arrow Right</span><button style="float: right;" onclick="setKey(this, 0)">Change</button></td>
|
||||
<td><span class="ps-right"></span>, Axis LeftX+</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Left</td>
|
||||
<td><span class="keyName">Arrow Left</span><button style="float: right;" onclick="setKey(this, 1)">Change</button></td>
|
||||
<td><span class="ps-left"></span>, Axis LeftX-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Down</td>
|
||||
<td><span class="keyName">Arrow Down</span><button style="float: right;" onclick="setKey(this, 2)">Change</button></td>
|
||||
<td><span class="ps-down"></span>, Axis LeftY+</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Up</td>
|
||||
<td><span class="keyName">Arrow Up</span><button style="float: right;" onclick="setKey(this, 3)">Change</button></td>
|
||||
<td><span class="ps-up"></span>, Axis LeftY-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>B</td>
|
||||
<td><span class="keyName">Key X</span><button style="float: right;" onclick="setKey(this, 4)">Change</button></td>
|
||||
<td><span class="ps-bg" style="text-align: center;"><span style="color: #91c85c; vertical-align: middle;">A</span></span> / <span class="ps-bg"><span class="ps-cross"></span></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>A</td>
|
||||
<td><span class="keyName">Key C</span><button style="float: right;" onclick="setKey(this, 5)">Change</button></td>
|
||||
<td><span class="ps-bg" style="text-align: center;"><span style="color: #0098d9; vertical-align: middle;">X</span></span> / <span class="ps-bg"><span class="ps-square"></span></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Select</td>
|
||||
<td><span class="keyName">Key Z</span><button style="float: right;" onclick="setKey(this, 6)">Change</button></td>
|
||||
<td>Back / <span class="ps-select"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Start</td>
|
||||
<td><span class="keyName">Space</span><button style="float: right;" onclick="setKey(this, 7)">Change</button></td>
|
||||
<td>Start / <span class="ps-start"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><span class="keyName"></span></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Reset</td>
|
||||
<td><span class="keyName">Tab</span><button style="float: right;" onclick="setKey(this, 9)">Change</button></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Save state</td>
|
||||
<td><span class="keyName">Digit 1</span><button style="float: right;" onclick="setKey(this, 10)">Change</button></td>
|
||||
<td>Left bumper / L1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Load state</td>
|
||||
<td><span class="keyName">Digit 2</span><button style="float: right;" onclick="setKey(this, 11)">Change</button></td>
|
||||
<td>Right bumper/ R1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Next palette</td>
|
||||
<td><span class="keyName">Key P</span><button style="float: right;" onclick="setKey(this, 12)">Change</button></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Window size -</td>
|
||||
<td><span class="keyName">Minus</span><button style="float: right;" onclick="setKey(this, 13)">Change</button></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Window size +</td>
|
||||
<td><span class="keyName">Equal</span><button style="float: right;" onclick="setKey(this, 14)">Change</button></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ghosting -</td>
|
||||
<td><span class="keyName">Bracket Left</span><button style="float: right;" onclick="setKey(this, 15)">Change</button></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ghosting +</td>
|
||||
<td><span class="keyName">Bracket Right</span><button style="float: right;" onclick="setKey(this, 16)">Change</button></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mute</td>
|
||||
<td><span class="keyName">Key M</span><button style="float: right;" onclick="setKey(this, 17)">Change</button></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h2>Savestates <span class="tooltip tooltipinfo" style="font-size: 0.7em;"><span style="font-weight: bolder;">!</span><span class="tooltiptext" style="width: 360px">The savestates exist in-memory. They are lost when the page is reloaded.</span></span></h2>
|
||||
<div style="margin: 10px auto;">
|
||||
<button onclick="refreshSavestates()">Refresh</button>
|
||||
<div style="display: inline;">
|
||||
<label for="ss_upload"><button onclick="parentNode.click()">Upload</button></label>
|
||||
<input type="file" id="ss_upload" onchange="uploadSavestate(event)" style="display: none;">
|
||||
</div>
|
||||
<!-- Lost preview-img's hover -->
|
||||
<div style="position: absolute; height: 10px; width: 80px; z-index: 1;"></div>
|
||||
</div>
|
||||
<table><tbody id="savestates"></tbody></table>
|
||||
|
||||
<h2>Information</h2>
|
||||
Source code: <a href="https://github.com/infval/potator">GitHub</a>.
|
||||
</div>
|
||||
<div class="emscripten" id="status" style="position: fixed; top: 0px; background-color: #eee; padding: 8px;">Downloading...</div>
|
||||
<div class="emscripten"><progress value="0" max="100" id="progress" style="display: none;"></progress></div>
|
||||
<textarea class="emscripten" id="output" rows="8" style="display: none;"></textarea>
|
||||
|
||||
<script type='text/javascript'>
|
||||
var statusElement = document.getElementById('status');
|
||||
var progressElement = document.getElementById('progress');
|
||||
var spinnerElement = document.getElementById('spinner');
|
||||
|
||||
var Module = {
|
||||
preRun: [],
|
||||
postRun: [],
|
||||
print: (function() {
|
||||
var element = document.getElementById('output');
|
||||
if (element) element.value = ''; // clear browser cache
|
||||
return function(text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
// These replacements are necessary if you render to raw HTML
|
||||
//text = text.replace(/&/g, "&");
|
||||
//text = text.replace(/</g, "<");
|
||||
//text = text.replace(/>/g, ">");
|
||||
//text = text.replace('\n', '<br>', 'g');
|
||||
console.log(text);
|
||||
if (element) {
|
||||
element.value += text + "\n";
|
||||
element.scrollTop = element.scrollHeight; // focus on bottom
|
||||
}
|
||||
};
|
||||
})(),
|
||||
printErr: function(text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
if (0) { // XXX disabled for safety typeof dump == 'function') {
|
||||
dump(text + '\n'); // fast, straight to the real console
|
||||
} else {
|
||||
console.error(text);
|
||||
}
|
||||
},
|
||||
canvas: (function() {
|
||||
var canvas = document.getElementById('canvas');
|
||||
|
||||
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
|
||||
// application robust, you may want to override this behavior before shipping!
|
||||
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
|
||||
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
|
||||
|
||||
return canvas;
|
||||
})(),
|
||||
setStatus: function(text) {
|
||||
if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
|
||||
if (text === Module.setStatus.last.text) return;
|
||||
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
|
||||
var now = Date.now();
|
||||
if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
|
||||
Module.setStatus.last.time = now;
|
||||
Module.setStatus.last.text = text;
|
||||
if (m) {
|
||||
text = m[1];
|
||||
progressElement.value = parseInt(m[2])*100;
|
||||
progressElement.max = parseInt(m[4])*100;
|
||||
progressElement.hidden = false;
|
||||
spinnerElement.hidden = false;
|
||||
} else {
|
||||
progressElement.value = null;
|
||||
progressElement.max = null;
|
||||
progressElement.hidden = true;
|
||||
if (!text) {
|
||||
spinnerElement.style.display = 'none';
|
||||
statusElement.style.display = 'none';
|
||||
}
|
||||
}
|
||||
statusElement.innerHTML = text;
|
||||
},
|
||||
totalDependencies: 0,
|
||||
monitorRunDependencies: function(left) {
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
|
||||
}
|
||||
};
|
||||
Module.setStatus('Downloading...');
|
||||
window.onerror = function() {
|
||||
Module.setStatus('Exception thrown, see JavaScript console');
|
||||
spinnerElement.style.display = 'none';
|
||||
Module.setStatus = function(text) {
|
||||
if (text) Module.printErr('[post-exception status] ' + text);
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<div id="dropzone"></div>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function onChange(event) {
|
||||
handleFiles(event.target.files);
|
||||
}
|
||||
function handleFiles(files) {
|
||||
var file = files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
//var buffer = Module._malloc(event.target.result.byteLength);
|
||||
//Module.writeArrayToMemory(new Uint8Array(event.target.result), buffer);
|
||||
//Module.ccall('UploadROM', 'void', ['number', 'number'], [buffer, event.target.result.byteLength]);
|
||||
//Module._free(buffer);
|
||||
var arr = new Uint8Array(event.target.result);
|
||||
Module.ccall('UploadROM', 'void', ['array', 'number', 'string'], [arr, arr.length, file.name]);
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
var dropzone = document.getElementById("dropzone");
|
||||
window.addEventListener("dragover", dragover, false);
|
||||
dropzone.addEventListener("dragleave", dragleave, false);
|
||||
dropzone.addEventListener("drop", drop, false);
|
||||
function dragover(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (e.target.id != "dropzone") {
|
||||
dropzone.style.display = "block";
|
||||
}
|
||||
}
|
||||
function dragleave(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (e.target.id == "dropzone") {
|
||||
dropzone.style.display = "none";
|
||||
}
|
||||
}
|
||||
function drop(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
dropzone.style.display = "none";
|
||||
handleFiles(e.dataTransfer.files);
|
||||
}
|
||||
|
||||
function createSlider(slider, oninput) {
|
||||
slider.addEventListener("input", oninput, false);
|
||||
slider.addEventListener("mouseup", (e) => {
|
||||
e.target.blur(); // FIX (Firefox), https://github.com/emscripten-ports/SDL2/issues/41
|
||||
}, false);
|
||||
}
|
||||
createSlider(document.getElementById("volumeslider"),
|
||||
(e) => { Module.ccall('SetVolume', 'void', ['number'], [e.target.value]); });
|
||||
createSlider(document.getElementById("ghostslider"),
|
||||
(e) => { Module.ccall('supervision_set_ghosting', 'void', ['number'], [e.target.value]); });
|
||||
|
||||
var newButtonId = -1;
|
||||
var ghostSlider = document.getElementById("ghostslider");
|
||||
var muteButton = document.getElementById("muteb");
|
||||
var keyCodes = [].map.call(document.getElementsByClassName("keyName"), (e) => e.textContent.replace(/ /g, ''));
|
||||
document.addEventListener("keydown", (e) => {
|
||||
switch (e.code) {
|
||||
case keyCodes[15]:
|
||||
ghostSlider.value -= 1;
|
||||
break;
|
||||
case keyCodes[16]:
|
||||
ghostSlider.value -= -1;
|
||||
break;
|
||||
case keyCodes[17]:
|
||||
muteButton.id = (muteButton.id == 'muteb' ? 'muteab' : 'muteb');
|
||||
break;
|
||||
}
|
||||
|
||||
endSetKey(e);
|
||||
});
|
||||
function setKey(element, buttonId) {
|
||||
if (newButtonId == -1) {
|
||||
element.textContent = "Press key";
|
||||
newButtonId = buttonId;
|
||||
Module.ccall('SetKey', 'void', ['int'], [newButtonId]);
|
||||
|
||||
pauseAudio(1);
|
||||
}
|
||||
}
|
||||
function endSetKey(keyboardEvent) {
|
||||
if (newButtonId != -1) {
|
||||
let keyNames = document.getElementsByClassName("keyName");
|
||||
if (keyboardEvent && keyboardEvent.code != "Escape") {
|
||||
keyCodes[newButtonId] = keyboardEvent.code;
|
||||
let newKeyName = keyboardEvent.code.replace(/([a-z])([\dA-Z])/g, '$1 $2');
|
||||
keyNames[newButtonId].textContent = newKeyName;
|
||||
}
|
||||
keyNames[newButtonId].nextElementSibling.textContent = "Change";
|
||||
newButtonId = -1;
|
||||
|
||||
if (keyboardEvent) pauseAudio(0);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("blur", function() {
|
||||
pauseAudio(1);
|
||||
endSetKey();
|
||||
}, false);
|
||||
window.addEventListener("focus", function() {
|
||||
pauseAudio(0);
|
||||
}, false);
|
||||
|
||||
// FIX (Chrome, ...?), project.js -> ASM_CONSTS, https://github.com/emscripten-ports/SDL2/blob/master/src/audio/emscripten/SDL_emscriptenaudio.c
|
||||
function pauseAudio(on) {
|
||||
if (typeof SDL2 === 'undefined') return;
|
||||
if (on) {
|
||||
SDL2.audio.scriptProcessorNode.disconnect();
|
||||
}
|
||||
else {
|
||||
SDL2.audio.scriptProcessorNode['connect'](SDL2.audioContext['destination']);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshSavestates() {
|
||||
var fileNames = FS.readdir(".").filter((e) => e.search(/\.svst$/) != -1);
|
||||
var ss = document.getElementById("savestates");
|
||||
ss.innerHTML = fileNames.map((e) =>
|
||||
'<tr>\
|
||||
<td>\
|
||||
' + e + '<button onclick="saveFile(\'' + e + '\')" style="float: right;">Download</button>\
|
||||
</td>\
|
||||
</tr>'
|
||||
).join('');
|
||||
|
||||
for (let i = 0; i < ss.children.length; i++) {
|
||||
let telem = document.createElement('td');
|
||||
let image = document.createElement('img');
|
||||
telem.setAttribute("class", "preview-td");
|
||||
image.setAttribute("class", "preview-img");
|
||||
drawPreview(fileNames[i], image);
|
||||
telem.appendChild(image);
|
||||
ss.children[i].insertBefore(telem, ss.children[i].firstChild);
|
||||
}
|
||||
}
|
||||
function saveFile(fileName) {
|
||||
saveAs(new Blob([FS.readFile(fileName)], {type: 'application/octet-stream'}), fileName);
|
||||
}
|
||||
// https://stackoverflow.com/q/23451726
|
||||
function saveAs(blob, fileName) {
|
||||
var url = window.URL.createObjectURL(blob);
|
||||
|
||||
var anchorElem = document.createElement("a");
|
||||
anchorElem.style = "display: none";
|
||||
anchorElem.href = url;
|
||||
anchorElem.download = fileName;
|
||||
|
||||
document.body.appendChild(anchorElem);
|
||||
anchorElem.click();
|
||||
|
||||
document.body.removeChild(anchorElem);
|
||||
|
||||
// On Edge, revokeObjectURL should be called only after
|
||||
// a.click() has completed, atleast on EdgeHTML 15.15048
|
||||
setTimeout(function() {
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 1000);
|
||||
}
|
||||
function uploadSavestate(event) {
|
||||
var file = event.target.files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
FS.writeFile(file.name, new Uint8Array(event.target.result));
|
||||
refreshSavestates();
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
function drawPreview(fileName, dstImage) {
|
||||
const WIDTH = 160, HEIGHT = 160;
|
||||
function setPixel(imageData, x, y, r, g, b) {
|
||||
var i = (x + y * d.width) * 4;
|
||||
imageData.data[i+0] = r;
|
||||
imageData.data[i+1] = g;
|
||||
imageData.data[i+2] = b;
|
||||
imageData.data[i+3] = 255;
|
||||
}
|
||||
function setPixelPalette(imageData, x, y, index) {
|
||||
const c = [240, 160, 80, 0];
|
||||
setPixel(imageData, x, y, c[index], c[index], c[index]);
|
||||
}
|
||||
var canvas = document.getElementById('canvas-preview');
|
||||
var ctx = canvas.getContext('2d');
|
||||
var d = ctx.createImageData(WIDTH, HEIGHT);
|
||||
var f = FS.readFile(fileName);
|
||||
var startRegs = 36 + 4;
|
||||
var startUpperRam = startRegs + 0x2000 * 2;
|
||||
var startVRAM = startUpperRam + f[startRegs + 2] / 4 + f[startRegs + 3] * 0x30;
|
||||
for (let y = 0; y < HEIGHT; y++) {
|
||||
for (let x = 0; x < WIDTH / 4; x++) {
|
||||
let b = f[startVRAM + y*0x30 + x];
|
||||
setPixelPalette(d, x*4 + 0, y, (b >> 0)&3);
|
||||
setPixelPalette(d, x*4 + 1, y, (b >> 2)&3);
|
||||
setPixelPalette(d, x*4 + 2, y, (b >> 4)&3);
|
||||
setPixelPalette(d, x*4 + 3, y, (b >> 6)&3);
|
||||
}
|
||||
}
|
||||
ctx.putImageData(d, 0, 0);
|
||||
|
||||
dstImage.src = canvas.toDataURL();
|
||||
}
|
||||
|
||||
function toggleFullscreen() {
|
||||
if (Browser.isFullscreen)
|
||||
Module.canvas.exitFullscreen();
|
||||
else
|
||||
Module.requestFullscreen(true, false);
|
||||
}
|
||||
</script>
|
||||
<canvas id="canvas-preview" width="160" height="160" style="border: 1px solid black; position: fixed; right: 0px; top: 0px; display: none;"></canvas>
|
||||
|
||||
<div style="height: 64px; margin-top: 32px; background-color: #fcfcfc; border-top: 2px solid #eee;">
|
||||
</div>
|
||||
{{{ SCRIPT }}}
|
||||
</body>
|
||||
</html>
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
@ -167,7 +167,7 @@ unsigned char potatorLoadROM(char* filename) {
|
||||
fread(rom_buffer, 1, rom_size, romfile);
|
||||
fclose(romfile);
|
||||
|
||||
supervision_load(rom_buffer, rom_size);
|
||||
supervision_load(&rom_buffer, rom_size);
|
||||
|
||||
// Compute game CRC
|
||||
gameCRC = crc32(0, rom_buffer, rom_size);
|
||||
|
@ -911,7 +911,7 @@ void menuSaveState(void) {
|
||||
strcpy(szFile, gameName);
|
||||
strcpy(strrchr(szFile, '.'), ".sta");
|
||||
print_string("Saving...", COLOR_OK, COLOR_BG, 8,240-5 -10*3);
|
||||
sv_saveState(szFile,1);
|
||||
supervision_save_state(szFile,1);
|
||||
print_string("Save OK",COLOR_OK,COLOR_BG, 8+10*8,240-5 -10*3);
|
||||
screen_flip();
|
||||
screen_waitkey();
|
||||
@ -926,7 +926,7 @@ void menuLoadState(void) {
|
||||
strcpy(szFile, gameName);
|
||||
strcpy(strrchr(szFile, '.'), ".sta");
|
||||
print_string("Loading...", COLOR_OK, COLOR_BG, 8,240-5 -10*3);
|
||||
sv_loadState(szFile,1);
|
||||
supervision_load_state(szFile,1);
|
||||
print_string("Load OK",COLOR_OK,COLOR_BG, 8+10*8,240-5 -10*3);
|
||||
screen_flip();
|
||||
screen_waitkey();
|
||||
|
Loading…
Reference in New Issue
Block a user