aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--frontend/cspace.h3
-rw-r--r--frontend/cspace_neon.S127
m---------frontend/libpicofe0
-rw-r--r--frontend/libretro.c7
-rw-r--r--frontend/menu.c82
-rw-r--r--frontend/menu.h2
-rw-r--r--frontend/plat_omap.c19
-rw-r--r--frontend/plugin_lib.c21
-rw-r--r--libpcsxcore/cdrom.c6
-rw-r--r--libpcsxcore/misc.c5
-rw-r--r--libpcsxcore/new_dynarec/emu_if.c50
-rw-r--r--libpcsxcore/new_dynarec/linkage_arm.S8
-rw-r--r--libpcsxcore/new_dynarec/new_dynarec.c354
-rw-r--r--libpcsxcore/new_dynarec/new_dynarec.h2
-rw-r--r--libpcsxcore/r3000a.h4
-rw-r--r--readme.txt23
17 files changed, 465 insertions, 249 deletions
diff --git a/Makefile b/Makefile
index 58de43a..339fcd5 100644
--- a/Makefile
+++ b/Makefile
@@ -165,6 +165,7 @@ OBJS += frontend/libpicofe/linux/fbdev.o frontend/libpicofe/linux/xenv.o
OBJS += frontend/libpicofe/linux/in_evdev.o
OBJS += frontend/plat_pandora.o frontend/plat_omap.o
frontend/main.o frontend/menu.o: CFLAGS += -include frontend/pandora/ui_feat.h
+frontend/libpicofe/linux/plat.o: CFLAGS += -DPANDORA
USE_PLUGIN_LIB = 1
USE_FRONTEND = 1
endif
diff --git a/frontend/cspace.h b/frontend/cspace.h
index 1a9e339..8c92d2d 100644
--- a/frontend/cspace.h
+++ b/frontend/cspace.h
@@ -8,6 +8,9 @@ void bgr888_to_rgb888(void *dst, const void *src, int bytes);
void bgr888_to_rgb565(void *dst, const void *src, int bytes);
void rgb888_to_rgb565(void *dst, const void *src, int bytes);
+void bgr555_to_rgb565_b(void *dst, const void *src, int bytes,
+ int brightness2k); // 0-0x0800
+
void bgr_to_uyvy_init(void);
void rgb565_to_uyvy(void *d, const void *s, int pixels);
void bgr555_to_uyvy(void *d, const void *s, int pixels);
diff --git a/frontend/cspace_neon.S b/frontend/cspace_neon.S
index 7420585..8b201db 100644
--- a/frontend/cspace_neon.S
+++ b/frontend/cspace_neon.S
@@ -19,7 +19,7 @@
.text
.align 2
-FUNCTION(bgr555_to_rgb565):
+FUNCTION(bgr555_to_rgb565): @ dst, src, bytes
pld [r1]
mov r3, #0x07c0
vdup.16 q15, r3
@@ -28,23 +28,23 @@ FUNCTION(bgr555_to_rgb565):
0:
pld [r1, #64*2]
vldmia r1!, {q0-q3}
- vshl.u16 q4, q0, #11
- vshl.u16 q5, q1, #11
- vshl.u16 q6, q2, #11
- vshl.u16 q7, q3, #11
- vsri.u16 q4, q0, #10
- vsri.u16 q5, q1, #10
- vsri.u16 q6, q2, #10
- vsri.u16 q7, q3, #10
- vshl.u16 q0, q0, #1
- vshl.u16 q1, q1, #1
- vshl.u16 q2, q2, #1
- vshl.u16 q3, q3, #1
- vbit q4, q0, q15
- vbit q5, q1, q15
- vbit q6, q2, q15
- vbit q7, q3, q15
- vstmia r0!, {q4-q7}
+ vshl.u16 q8, q0, #11
+ vshl.u16 q9, q1, #11
+ vshl.u16 q10, q2, #11
+ vshl.u16 q11, q3, #11
+ vsri.u16 q8, q0, #10
+ vsri.u16 q9, q1, #10
+ vsri.u16 q10, q2, #10
+ vsri.u16 q11, q3, #10
+ vshl.u16 q0, q0, #1
+ vshl.u16 q1, q1, #1
+ vshl.u16 q2, q2, #1
+ vshl.u16 q3, q3, #1
+ vbit q8, q0, q15
+ vbit q9, q1, q15
+ vbit q10, q2, q15
+ vbit q11, q3, q15
+ vstmia r0!, {q8-q11}
subs r2, r2, #64
bge 0b
@@ -72,16 +72,97 @@ btr16_end16:
bxlt lr
@ very rare
- vld1.16 d0, [r1]!
+ vld1.16 {d0}, [r1]!
vshl.u16 d1, d0, #11
vshl.u16 d2, d0, #1
vsri.u16 d1, d0, #10
vbit d1, d2, d30
- vst1.16 d1, [r0]!
+ vst1.16 {d1}, [r0]!
+ bx lr
+
+
+@ note: may overflow source
+FUNCTION(bgr555_to_rgb565_b): @ dst, src, bytes, int brightness2k // 0-0x0800
+ pld [r1]
+ vdup.16 q15, r3
+ vpush {q4-q7}
+ mov r3, #0x1f
+ vdup.16 q14, r3
+0:
+ pld [r1, #64*2]
+ vldmia r1!, {q0-q3}
+ vand.u16 q8, q0, q14
+ vand.u16 q9, q1, q14
+ vand.u16 q10, q2, q14
+ vand.u16 q11, q3, q14
+ vmul.u16 q4, q8, q15
+ vmul.u16 q5, q9, q15
+ vmul.u16 q6, q10, q15
+ vmul.u16 q7, q11, q15
+
+ vshr.u16 q8, q0, #5
+ vshr.u16 q9, q1, #5
+ vshr.u16 q10, q2, #5
+ vshr.u16 q11, q3, #5
+ vand.u16 q8, q14
+ vand.u16 q9, q14
+ vand.u16 q10, q14
+ vand.u16 q11, q14
+ vmul.u16 q8, q15
+ vmul.u16 q9, q15
+ vmul.u16 q10, q15
+ vmul.u16 q11, q15
+ vsri.u16 q4, q8, #5
+ vsri.u16 q5, q9, #5
+ vsri.u16 q6, q10, #5
+ vsri.u16 q7, q11, #5
+
+ vshr.u16 q8, q0, #10
+ vshr.u16 q9, q1, #10
+ vshr.u16 q10, q2, #10
+ vshr.u16 q11, q3, #10
+ vand.u16 q8, q14
+ vand.u16 q9, q14
+ vand.u16 q10, q14
+ vand.u16 q11, q14
+ vmul.u16 q8, q15
+ vmul.u16 q9, q15
+ vmul.u16 q10, q15
+ vmul.u16 q11, q15
+ vsri.u16 q4, q8, #11
+ vsri.u16 q5, q9, #11
+ vsri.u16 q6, q10, #11
+ vsri.u16 q7, q11, #11
+
+ subs r2, r2, #64
+ ble 1f
+ vstmia r0!, {q4-q7}
+ b 0b
+
+1:
+ blt 0f
+ vstmia r0!, {q4-q7}
+ b btr16b_end
+0:
+ subs r2, r2, #8
+ blt btr16b_end
+ vst1.16 {q4}, [r0]!
+ subs r2, r2, #8
+ blt btr16b_end
+ vst1.16 {q5}, [r0]!
+ subs r2, r2, #8
+ blt btr16b_end
+ vst1.16 {q6}, [r0]!
+ subs r2, r2, #8
+ blt btr16b_end
+ vst1.16 {q7}, [r0]!
+
+btr16b_end:
+ vpop {q4-q7}
bx lr
-FUNCTION(bgr888_to_rgb888):
+FUNCTION(bgr888_to_rgb888): @ dst, src, bytes
pld [r1]
@ r2 /= 48
mov r2, r2, lsr #4
@@ -102,7 +183,7 @@ FUNCTION(bgr888_to_rgb888):
bx lr
-FUNCTION(bgr888_to_rgb565):
+FUNCTION(bgr888_to_rgb565): @ dst, src, bytes
pld [r1]
@ r2 /= 48
mov r2, r2, lsr #4
@@ -134,7 +215,7 @@ FUNCTION(bgr888_to_rgb565):
bx lr
-FUNCTION(rgb888_to_rgb565):
+FUNCTION(rgb888_to_rgb565): @ dst, src, bytes
pld [r1]
@ r2 /= 48
mov r2, r2, lsr #4
diff --git a/frontend/libpicofe b/frontend/libpicofe
-Subproject d1453cf7e6d5d6758cc5d72c6d3af7d37156bf7
+Subproject 515ac0b9d2c4d45a465335d54b8c49830914fce
diff --git a/frontend/libretro.c b/frontend/libretro.c
index fa544fd..b636f49 100644
--- a/frontend/libretro.c
+++ b/frontend/libretro.c
@@ -283,7 +283,7 @@ void retro_get_system_info(struct retro_system_info *info)
{
memset(info, 0, sizeof(*info));
info->library_name = "PCSX-ReARMed";
- info->library_version = "r19";
+ info->library_version = "r20";
info->valid_extensions = "bin|cue|img|mdf|pbp|toc|cbn|m3u";
info->need_fullpath = true;
}
@@ -303,8 +303,9 @@ void retro_get_system_av_info(struct retro_system_av_info *info)
/* savestates */
size_t retro_serialize_size(void)
{
- // it's currently 4380651 bytes, but have some reserved for future
- return 0x430000;
+ // it's currently 4380651-4397047 bytes,
+ // but have some reserved for future
+ return 0x440000;
}
struct save_fp {
diff --git a/frontend/menu.c b/frontend/menu.c
index 1562735..a7012e6 100644
--- a/frontend/menu.c
+++ b/frontend/menu.c
@@ -1,5 +1,5 @@
/*
- * (C) Gražvydas "notaz" Ignotas, 2010-2013
+ * (C) Gražvydas "notaz" Ignotas, 2010-2014
*
* This work is licensed under the terms of any of these licenses
* (at your option):
@@ -83,6 +83,8 @@ typedef enum
MA_OPT_SWFILTER,
MA_OPT_GAMMA,
MA_OPT_VOUT_MODE,
+ MA_OPT_SCANLINES,
+ MA_OPT_SCANLINE_LEVEL,
} menu_id;
static int last_vout_w, last_vout_h, last_vout_bpp;
@@ -90,8 +92,10 @@ static int cpu_clock, cpu_clock_st, volume_boost, frameskip;
static char last_selected_fname[MAXPATHLEN];
static int config_save_counter, region, in_type_sel1, in_type_sel2;
static int psx_clock;
-static int memcard1_sel, memcard2_sel;
+static int memcard1_sel = -1, memcard2_sel = -1;
+extern int g_autostateld_opt;
int g_opts, g_scaler, g_gamma = 100;
+int scanlines, scanline_level = 20;
int soft_scaling, analog_deadzone; // for Caanoo
int soft_filter;
@@ -209,6 +213,9 @@ static int optional_cdimg_filter(struct dirent **namelist, int count,
struct stat64 statf;
FILE *f;
+ if (count <= 1)
+ return count;
+
for (i = 1; i < count; i++) {
if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
continue;
@@ -330,6 +337,8 @@ static void menu_set_defconfig(void)
analog_deadzone = 50;
soft_scaling = 1;
soft_filter = 0;
+ scanlines = 0;
+ scanline_level = 20;
plat_target.vout_fullscreen = 0;
psx_clock = DEFAULT_PSX_CLOCK;
@@ -388,13 +397,15 @@ static const struct {
CE_CONFIG_VAL(VSyncWA),
CE_CONFIG_VAL(Cpu),
CE_INTVAL(region),
- CE_INTVAL_V(g_scaler, 2),
+ CE_INTVAL_V(g_scaler, 3),
CE_INTVAL(g_gamma),
CE_INTVAL(g_layer_x),
CE_INTVAL(g_layer_y),
CE_INTVAL(g_layer_w),
CE_INTVAL(g_layer_h),
CE_INTVAL(soft_filter),
+ CE_INTVAL(scanlines),
+ CE_INTVAL(scanline_level),
CE_INTVAL(plat_target.vout_method),
CE_INTVAL(plat_target.hwfilter),
CE_INTVAL(plat_target.vout_fullscreen),
@@ -404,6 +415,9 @@ static const struct {
CE_INTVAL(in_type_sel1),
CE_INTVAL(in_type_sel2),
CE_INTVAL(analog_deadzone),
+ CE_INTVAL(memcard1_sel),
+ CE_INTVAL(memcard2_sel),
+ CE_INTVAL(g_autostateld_opt),
CE_INTVAL_N("adev0_is_nublike", in_adev_is_nublike[0]),
CE_INTVAL_N("adev1_is_nublike", in_adev_is_nublike[1]),
CE_INTVAL_V(frameskip, 3),
@@ -661,6 +675,29 @@ fail:
if (strcmp(Config.Spu, spu_plugins[i]) == 0)
{ spu_plugsel = i; break; }
+ // memcard selections
+ char mcd1_old[sizeof(Config.Mcd1)];
+ char mcd2_old[sizeof(Config.Mcd2)];
+ strcpy(mcd1_old, Config.Mcd1);
+ strcpy(mcd2_old, Config.Mcd2);
+
+ if ((unsigned int)memcard1_sel < ARRAY_SIZE(memcards)) {
+ if (memcard1_sel == 0)
+ strcpy(Config.Mcd1, "none");
+ else if (memcards[memcard1_sel] != NULL)
+ snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s",
+ MEMCARD_DIR, memcards[memcard1_sel]);
+ }
+ if ((unsigned int)memcard2_sel < ARRAY_SIZE(memcards)) {
+ if (memcard2_sel == 0)
+ strcpy(Config.Mcd2, "none");
+ else if (memcards[memcard2_sel] != NULL)
+ snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s",
+ MEMCARD_DIR, memcards[memcard2_sel]);
+ }
+ if (strcmp(mcd1_old, Config.Mcd1) || strcmp(mcd2_old, Config.Mcd2))
+ LoadMcds(Config.Mcd1, Config.Mcd2);
+
return ret;
}
@@ -1191,17 +1228,22 @@ static int menu_loop_keyconfig(int id, int keys)
// ------------ gfx options menu ------------
-static const char *men_scaler[] = { "1x1", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL };
+static const char *men_scaler[] = {
+ "1x1", "integer scaled 2x", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL
+};
static const char *men_soft_filter[] = { "None",
#ifdef __ARM_NEON__
"scale2x", "eagle2x",
#endif
NULL };
static const char *men_dummy[] = { NULL };
+static const char h_scaler[] = "int. 2x - scales w. or h. 2x if it fits on screen\n"
+ "int. 4:3 - uses integer if possible, else fractional";
static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
"using d-pad or move it using R+d-pad";
static const char h_overlay[] = "Overlay provides hardware accelerated scaling";
static const char h_soft_filter[] = "Works only if game uses low resolution modes";
+static const char h_scanline_l[] = "Scanline brightness, 0-100%";
static const char h_gamma[] = "Gamma/brightness adjustment (default 100)";
static int menu_loop_cscaler(int id, int keys)
@@ -1258,11 +1300,15 @@ static int menu_loop_cscaler(int id, int keys)
static menu_entry e_menu_gfx_options[] =
{
- mee_enum ("Scaler", MA_OPT_VARSCALER, g_scaler, men_scaler),
+ mee_enum_h ("Scaler", MA_OPT_VARSCALER, g_scaler, men_scaler, h_scaler),
mee_enum ("Video output mode", MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
mee_enum ("Hardware Filter", MA_OPT_HWFILTER, plat_target.hwfilter, men_dummy),
mee_enum_h ("Software Filter", MA_OPT_SWFILTER, soft_filter, men_soft_filter, h_soft_filter),
+#ifdef __ARM_NEON__
+ mee_onoff ("Scanlines", MA_OPT_SCANLINES, scanlines, 1),
+ mee_range_h ("Scanline brightness", MA_OPT_SCANLINE_LEVEL, scanline_level, 0, 100, h_scanline_l),
+#endif
mee_range_h ("Gamma adjustment", MA_OPT_GAMMA, g_gamma, 1, 200, h_gamma),
// mee_onoff ("Vsync", 0, vsync, 1),
mee_cust_h ("Setup custom scaler", MA_OPT_VARSCALER_C, menu_loop_cscaler, NULL, h_cscaler),
@@ -1882,7 +1928,7 @@ static const char credits_text[] =
"PCSX4ALL plugin by PCSX4ALL team\n"
" Chui, Franxis, Unai\n\n"
"integration, optimization and\n"
- " frontend (C) 2010-2012 notaz\n";
+ " frontend (C) 2010-2014 notaz\n";
static int reset_game(void)
{
@@ -1963,6 +2009,8 @@ static int run_exe(void)
static int run_cd_image(const char *fname)
{
+ int autoload_state = g_autostateld_opt;
+
ready_to_go = 0;
reload_plugins(fname);
@@ -1988,6 +2036,28 @@ static int run_cd_image(const char *fname)
emu_on_new_cd(1);
ready_to_go = 1;
+ if (autoload_state) {
+ unsigned int newest = 0;
+ int time, slot, newest_slot = -1;
+
+ for (slot = 0; slot < 10; slot++) {
+ if (emu_check_save_file(slot, &time)) {
+ if ((unsigned int)time > newest) {
+ newest = time;
+ newest_slot = slot;
+ }
+ }
+ }
+
+ if (newest_slot >= 0) {
+ lprintf("autoload slot %d\n", newest_slot);
+ emu_load_state(newest_slot);
+ }
+ else {
+ lprintf("no save to autoload.\n");
+ }
+ }
+
return 0;
}
diff --git a/frontend/menu.h b/frontend/menu.h
index 0d68469..81cd1ba 100644
--- a/frontend/menu.h
+++ b/frontend/menu.h
@@ -15,6 +15,7 @@ enum g_opts_opts {
enum g_scaler_opts {
SCALE_1_1,
+ SCALE_2_2,
SCALE_4_3,
SCALE_4_3v2,
SCALE_FULLSCREEN,
@@ -28,6 +29,7 @@ enum g_soft_filter_opts {
};
extern int g_opts, g_scaler, g_gamma;
+extern int scanlines, scanline_level;
extern int soft_scaling, analog_deadzone;
extern int soft_filter;
diff --git a/frontend/plat_omap.c b/frontend/plat_omap.c
index 6126140..4e3ea79 100644
--- a/frontend/plat_omap.c
+++ b/frontend/plat_omap.c
@@ -94,20 +94,25 @@ void plat_omap_gvideo_open(void)
vout_fbdev_wait_vsync(layer_fb);
}
-void *plat_gvideo_set_mode(int *w, int *h, int *bpp)
+void *plat_gvideo_set_mode(int *w_in, int *h_in, int *bpp)
{
int l = 0, r = 0, t = 0, b = 0;
+ int w = *w_in, h = *h_in;
void *buf;
- if (g_scaler == SCALE_1_1) {
- if (*w > g_menuscreen_w)
- l = r = (*w - g_menuscreen_w) / 2;
- if (*h > g_menuscreen_h)
- t = b = (*h - g_menuscreen_h) / 2;
+ if (g_scaler == SCALE_1_1 || g_scaler == SCALE_2_2) {
+ if (w > g_menuscreen_w) {
+ l = r = (w - g_menuscreen_w) / 2;
+ w -= l + r;
+ }
+ if (h > g_menuscreen_h) {
+ t = b = (h - g_menuscreen_h) / 2;
+ h -= t + b;
+ }
}
vout_fbdev_clear(layer_fb);
- buf = vout_fbdev_resize(layer_fb, *w, *h, *bpp,
+ buf = vout_fbdev_resize(layer_fb, w, h, *bpp,
l, r, t, b, 3);
omap_enable_layer(1);
diff --git a/frontend/plugin_lib.c b/frontend/plugin_lib.c
index 72b3395..163d4f1 100644
--- a/frontend/plugin_lib.c
+++ b/frontend/plugin_lib.c
@@ -180,6 +180,14 @@ static void update_layer_size(int w, int h)
g_layer_w = w; g_layer_h = h;
break;
+ case SCALE_2_2:
+ g_layer_w = w; g_layer_h = h;
+ if (w * 2 <= g_menuscreen_w)
+ g_layer_w = w * 2;
+ if (h * 2 <= g_menuscreen_h)
+ g_layer_h = h * 2;
+ break;
+
case SCALE_4_3v2:
if (h > g_menuscreen_h || (240 < h && h <= 360))
goto fractional_4_3;
@@ -363,6 +371,19 @@ static void pl_vout_flip(const void *vram, int stride, int bgr24, int w, int h)
neon_eagle2x_16_16(src, (void *)dest, w,
stride * 2, dstride * 2, h);
}
+ else if (scanlines != 0 && scanline_level != 100)
+ {
+ int l = scanline_level * 2048 / 100;
+
+ for (; h1 >= 2; h1 -= 2)
+ {
+ bgr555_to_rgb565(dest, src, w * 2);
+ dest += dstride * 2, src += stride;
+
+ bgr555_to_rgb565_b(dest, src, w * 2, l);
+ dest += dstride * 2, src += stride;
+ }
+ }
#endif
else
{
diff --git a/libpcsxcore/cdrom.c b/libpcsxcore/cdrom.c
index 38fecf7..b686855 100644
--- a/libpcsxcore/cdrom.c
+++ b/libpcsxcore/cdrom.c
@@ -1514,6 +1514,12 @@ int cdrFreeze(void *f, int Mode) {
SysPrintf("cdrom: fixing up old savestate\n");
cdr.Reg2 = 7;
}
+ // also did not save Attenuator..
+ if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
+ | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
+ {
+ cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
+ }
}
}
diff --git a/libpcsxcore/misc.c b/libpcsxcore/misc.c
index 3ee9876..58170cf 100644
--- a/libpcsxcore/misc.c
+++ b/libpcsxcore/misc.c
@@ -573,7 +573,7 @@ int SaveState(const char *file) {
f = SaveFuncs.open(file, "wb");
if (f == NULL) return -1;
- new_dyna_save();
+ new_dyna_before_save();
SaveFuncs.write(f, (void *)PcsxHeader, 32);
SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
@@ -615,6 +615,7 @@ int SaveState(const char *file) {
psxHwFreeze(f, 1);
psxRcntFreeze(f, 1);
mdecFreeze(f, 1);
+ new_dyna_freeze(f, 1);
SaveFuncs.close(f);
@@ -679,9 +680,9 @@ int LoadState(const char *file) {
psxHwFreeze(f, 0);
psxRcntFreeze(f, 0);
mdecFreeze(f, 0);
+ new_dyna_freeze(f, 0);
SaveFuncs.close(f);
- new_dyna_restore();
return 0;
}
diff --git a/libpcsxcore/new_dynarec/emu_if.c b/libpcsxcore/new_dynarec/emu_if.c
index 89e2bd6..b7a2489 100644
--- a/libpcsxcore/new_dynarec/emu_if.c
+++ b/libpcsxcore/new_dynarec/emu_if.c
@@ -122,7 +122,7 @@ void pcsx_mtc0_ds(u32 reg, u32 val)
MTC0(reg, val);
}
-void new_dyna_save(void)
+void new_dyna_before_save(void)
{
psxRegs.interrupt &= ~(1 << PSXINT_RCNT); // old savestate compat
@@ -134,7 +134,7 @@ void new_dyna_after_save(void)
psxRegs.interrupt |= 1 << PSXINT_RCNT;
}
-void new_dyna_restore(void)
+static void new_dyna_restore(void)
{
int i;
for (i = 0; i < PSXINT_COUNT; i++)
@@ -147,6 +147,50 @@ void new_dyna_restore(void)
new_dyna_pcsx_mem_load_state();
}
+void new_dyna_freeze(void *f, int mode)
+{
+ const char header_save[8] = "ariblks";
+ uint32_t addrs[1024 * 4];
+ int32_t size = 0;
+ int bytes;
+ char header[8];
+
+ if (mode != 0) { // save
+ size = new_dynarec_save_blocks(addrs, sizeof(addrs));
+ if (size == 0)
+ return;
+
+ SaveFuncs.write(f, header_save, sizeof(header_save));
+ SaveFuncs.write(f, &size, sizeof(size));
+ SaveFuncs.write(f, addrs, size);
+ }
+ else {
+ new_dyna_restore();
+
+ bytes = SaveFuncs.read(f, header, sizeof(header));
+ if (bytes != sizeof(header) || strcmp(header, header_save)) {
+ if (bytes > 0)
+ SaveFuncs.seek(f, -bytes, SEEK_CUR);
+ return;
+ }
+ SaveFuncs.read(f, &size, sizeof(size));
+ if (size <= 0)
+ return;
+ if (size > sizeof(addrs)) {
+ bytes = size - sizeof(addrs);
+ SaveFuncs.seek(f, bytes, SEEK_CUR);
+ size = sizeof(addrs);
+ }
+ bytes = SaveFuncs.read(f, addrs, size);
+ if (bytes != size)
+ return;
+
+ new_dynarec_load_blocks(addrs, size);
+ }
+
+ //printf("drc: %d block info entries %s\n", size/8, mode ? "saved" : "loaded");
+}
+
/* GTE stuff */
void *gte_handlers[64];
@@ -407,6 +451,8 @@ void new_dyna_pcsx_mem_init(void) {}
void new_dyna_pcsx_mem_reset(void) {}
void new_dyna_pcsx_mem_load_state(void) {}
void new_dyna_pcsx_mem_shutdown(void) {}
+int new_dynarec_save_blocks(void *save, int size) { return 0; }
+void new_dynarec_load_blocks(const void *save, int size) {}
#endif
#ifdef DRC_DBG
diff --git a/libpcsxcore/new_dynarec/linkage_arm.S b/libpcsxcore/new_dynarec/linkage_arm.S
index 942b492..50b577b 100644
--- a/libpcsxcore/new_dynarec/linkage_arm.S
+++ b/libpcsxcore/new_dynarec/linkage_arm.S
@@ -184,14 +184,10 @@ ptr_hash_table:
1:
movs r4, r5
beq 2f
- ldr r3, [r5]
- ldr r5, [r4, #12]
+ ldr r3, [r5] /* ll_entry .vaddr */
+ ldrd r4, r5, [r4, #8] /* ll_entry .next, .addr */
teq r3, r0
bne 1b
- ldr r3, [r4, #4]
- ldr r4, [r4, #8]
- tst r3, r3
- bne 1b
teq r4, r6
moveq pc, r4 /* Stale i-cache */
mov r8, r4
diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c
index d8d8991..5120df0 100644
--- a/libpcsxcore/new_dynarec/new_dynarec.c
+++ b/libpcsxcore/new_dynarec/new_dynarec.c
@@ -74,17 +74,17 @@ struct regstat
u_int waswritten; // MIPS regs that were used as store base before
};
+// note: asm depends on this layout
struct ll_entry
{
u_int vaddr;
- u_int reg32;
+ u_int reg_sv_flags;
void *addr;
struct ll_entry *next;
};
u_int start;
u_int *source;
- u_int pagelimit;
char insn[MAXBLOCK][10];
u_char itype[MAXBLOCK];
u_char opcode[MAXBLOCK];
@@ -140,7 +140,7 @@ struct ll_entry
int is_delayslot;
int cop1_usable;
u_char *out;
- struct ll_entry *jump_in[4096];
+ struct ll_entry *jump_in[4096] __attribute__((aligned(16)));
struct ll_entry *jump_out[4096];
struct ll_entry *jump_dirty[4096];
u_int hash_table[65536][4] __attribute__((aligned(16)));
@@ -395,7 +395,7 @@ void *get_addr(u_int vaddr)
//printf("TRACE: count=%d next=%d (get_addr %x,page %d)\n",Count,next_interupt,vaddr,page);
head=jump_in[page];
while(head!=NULL) {
- if(head->vaddr==vaddr&&head->reg32==0) {
+ if(head->vaddr==vaddr) {
//printf("TRACE: count=%d next=%d (get_addr match %x: %x)\n",Count,next_interupt,vaddr,(int)head->addr);
int *ht_bin=hash_table[((vaddr>>16)^vaddr)&0xFFFF];
ht_bin[3]=ht_bin[1];
@@ -408,7 +408,7 @@ void *get_addr(u_int vaddr)
}
head=jump_dirty[vpage];
while(head!=NULL) {
- if(head->vaddr==vaddr&&head->reg32==0) {
+ if(head->vaddr==vaddr) {
//printf("TRACE: count=%d next=%d (get_addr match dirty %x: %x)\n",Count,next_interupt,vaddr,(int)head->addr);
// Don't restore blocks which are about to expire from the cache
if((((u_int)head->addr-(u_int)out)<<(32-TARGET_SIZE_2))>0x60000000+(MAX_OUTPUT_BLOCK_SIZE<<(32-TARGET_SIZE_2)))
@@ -467,94 +467,6 @@ void *get_addr_ht(u_int vaddr)
return get_addr(vaddr);
}
-void *get_addr_32(u_int vaddr,u_int flags)
-{
-#ifdef FORCE32
- return get_addr(vaddr);
-#else
- //printf("TRACE: count=%d next=%d (get_addr_32 %x,flags %x)\n",Count,next_interupt,vaddr,flags);
- int *ht_bin=hash_table[((vaddr>>16)^vaddr)&0xFFFF];
- if(ht_bin[0]==vaddr) return (void *)ht_bin[1];
- if(ht_bin[2]==vaddr) return (void *)ht_bin[3];
- u_int page=get_page(vaddr);
- u_int vpage=get_vpage(vaddr);
- struct ll_entry *head;
- head=jump_in[page];
- while(head!=NULL) {
- if(head->vaddr==vaddr&&(head->reg32&flags)==0) {
- //printf("TRACE: count=%d next=%d (get_addr_32 match %x: %x)\n",Count,next_interupt,vaddr,(int)head->addr);
- if(head->reg32==0) {
- int *ht_bin=hash_table[((vaddr>>16)^vaddr)&0xFFFF];
- if(ht_bin[0]==-1) {
- ht_bin[1]=(int)head->addr;
- ht_bin[0]=vaddr;
- }else if(ht_bin[2]==-1) {
- ht_bin[3]=(int)head->addr;
- ht_bin[2]=vaddr;
- }
- //ht_bin[3]=ht_bin[1];
- //ht_bin[2]=ht_bin[0];
- //ht_bin[1]=(int)head->addr;
- //ht_bin[0]=vaddr;
- }
- return head->addr;
- }
- head=head->next;
- }
- head=jump_dirty[vpage];
- while(head!=NULL) {
- if(head->vaddr==vaddr&&(head->reg32&flags)==0) {
- //printf("TRACE: count=%d next=%d (get_addr_32 match dirty %x: %x)\n",Count,next_interupt,vaddr,(int)head->addr);
- // Don't restore blocks which are about to expire from the cache
- if((((u_int)head->addr-(u_int)out)<<(32-TARGET_SIZE_2))>0x60000000+(MAX_OUTPUT_BLOCK_SIZE<<(32-TARGET_SIZE_2)))
- if(verify_dirty(head->addr)) {
- //printf("restore candidate: %x (%d) d=%d\n",vaddr,page,invalid_code[vaddr>>12]);
- invalid_code[vaddr>>12]=0;
- inv_code_start=inv_code_end=~0;
- memory_map[vaddr>>12]|=0x40000000;
- if(vpage<2048) {
-#ifndef DISABLE_TLB
- if(tlb_LUT_r[vaddr>>12]) {
- invalid_code[tlb_LUT_r[vaddr>>12]>>12]=0;
- memory_map[tlb_LUT_r[vaddr>>12]>>12]|=0x40000000;
- }
-#endif
- restore_candidate[vpage>>3]|=1<<(vpage&7);
- }
- else restore_candidate[page>>3]|=1<<(page&7);
- if(head->reg32==0) {
- int *ht_bin=hash_table[((vaddr>>16)^vaddr)&0xFFFF];
- if(ht_bin[0]==-1) {
- ht_bin[1]=(int)head->addr;
- ht_bin[0]=vaddr;
- }else if(ht_bin[2]==-1) {
- ht_bin[3]=(int)head->addr;
- ht_bin[2]=vaddr;
- }
- //ht_bin[3]=ht_bin[1];
- //ht_bin[2]=ht_bin[0];
- //ht_bin[1]=(int)head->addr;
- //ht_bin[0]=vaddr;
- }
- return head->addr;
- }
- }
- head=head->next;
- }
- //printf("TRACE: count=%d next=%d (get_addr_32 no-match %x,flags %x)\n",Count,next_interupt,vaddr,flags);
- int r=new_recompile_block(vaddr);
- if(r==0) return get_addr(vaddr);
- // Execute in unmapped page, generate pagefault execption
- Status|=2;
- Cause=(vaddr<<31)|0x8;
- EPC=(vaddr&1)?vaddr-5:vaddr;
- BadVAddr=(vaddr&~1);
- Context=(Context&0xFF80000F)|((BadVAddr>>9)&0x007FFFF0);
- EntryHi=BadVAddr&0xFFFFE000;
- return get_addr_ht(0x80000000);
-#endif
-}
-
void clear_all_regs(signed char regmap[])
{
int hr;
@@ -1020,19 +932,16 @@ void ll_add(struct ll_entry **head,int vaddr,void *addr)
new_entry=malloc(sizeof(struct ll_entry));
assert(new_entry!=NULL);
new_entry->vaddr=vaddr;
- new_entry->reg32=0;
+ new_entry->reg_sv_flags=0;
new_entry->addr=addr;
new_entry->next=*head;
*head=new_entry;
}
-// Add virtual address mapping for 32-bit compiled block
-void ll_add_32(struct ll_entry **head,int vaddr,u_int reg32,void *addr)
+void ll_add_flags(struct ll_entry **head,int vaddr,u_int reg_sv_flags,void *addr)
{
ll_add(head,vaddr,addr);
-#ifndef FORCE32
- (*head)->reg32=reg32;
-#endif
+ (*head)->reg_sv_flags=reg_sv_flags;
}
// Check if an address is already compiled
@@ -1052,7 +961,7 @@ void *check_addr(u_int vaddr)
struct ll_entry *head;
head=jump_in[page];
while(head!=NULL) {
- if(head->vaddr==vaddr&&head->reg32==0) {
+ if(head->vaddr==vaddr) {
if((((u_int)head->addr-(u_int)out)<<(32-TARGET_SIZE_2))>0x60000000+(MAX_OUTPUT_BLOCK_SIZE<<(32-TARGET_SIZE_2))) {
// Update existing entry with current address
if(ht_bin[0]==vaddr) {
@@ -1401,15 +1310,13 @@ void clean_blocks(u_int page)
inv_debug("INV: Restored %x (%x/%x)\n",head->vaddr, (int)head->addr, (int)clean_addr);
//printf("page=%x, addr=%x\n",page,head->vaddr);
//assert(head->vaddr>>12==(page|0x80000));
- ll_add_32(jump_in+ppage,head->vaddr,head->reg32,clean_addr);
+ ll_add_flags(jump_in+ppage,head->vaddr,head->reg_sv_flags,clean_addr);
int *ht_bin=hash_table[((head->vaddr>>16)^head->vaddr)&0xFFFF];
- if(!head->reg32) {
- if(ht_bin[0]==head->vaddr) {
- ht_bin[1]=(int)clean_addr; // Replace existing entry
- }
- if(ht_bin[2]==head->vaddr) {
- ht_bin[3]=(int)clean_addr; // Replace existing entry
- }
+ if(ht_bin[0]==head->vaddr) {
+ ht_bin[1]=(int)clean_addr; // Replace existing entry
+ }
+ if(ht_bin[2]==head->vaddr) {
+ ht_bin[3]=(int)clean_addr; // Replace existing entry
}
}
}
@@ -8151,17 +8058,132 @@ void new_dynarec_cleanup()
#endif
}
-int new_recompile_block(int addr)
+static u_int *get_source_start(u_int addr, u_int *limit)
{
-/*
- if(addr==0x800cd050) {
- int block;
- for(block=0x80000;block<0x80800;block++) invalidate_block(block);
- int n;
- for(n=0;n<=2048;n++) ll_clear(jump_dirty+n);
+ if (addr < 0x00200000 ||
+ (0xa0000000 <= addr && addr < 0xa0200000)) {
+ // used for BIOS calls mostly?
+ *limit = (addr&0xa0000000)|0x00200000;
+ return (u_int *)((u_int)rdram + (addr&0x1fffff));
+ }
+ else if (!Config.HLE && (
+ /* (0x9fc00000 <= addr && addr < 0x9fc80000) ||*/
+ (0xbfc00000 <= addr && addr < 0xbfc80000))) {
+ // BIOS
+ *limit = (addr & 0xfff00000) | 0x80000;
+ return (u_int *)((u_int)psxR + (addr&0x7ffff));
+ }
+ else if (addr >= 0x80000000 && addr < 0x80000000+RAM_SIZE) {
+ *limit = (addr & 0x80600000) + 0x00200000;
+ return (u_int *)((u_int)rdram + (addr&0x1fffff));
+ }
+}
+
+static u_int scan_for_ret(u_int addr)
+{
+ u_int limit = 0;
+ u_int *mem;
+
+ mem = get_source_start(addr, &limit);
+ if (mem == NULL)
+ return addr;
+
+ if (limit > addr + 0x1000)
+ limit = addr + 0x1000;
+ for (; addr < limit; addr += 4, mem++) {
+ if (*mem == 0x03e00008) // jr $ra
+ return addr + 8;
+ }
+}
+
+struct savestate_block {
+ uint32_t addr;
+ uint32_t regflags;
+};
+
+static int addr_cmp(const void *p1_, const void *p2_)
+{
+ const struct savestate_block *p1 = p1_, *p2 = p2_;
+ return p1->addr - p2->addr;
+}
+
+int new_dynarec_save_blocks(void *save, int size)
+{
+ struct savestate_block *blocks = save;
+ int maxcount = size / sizeof(blocks[0]);
+ struct savestate_block tmp_blocks[1024];
+ struct ll_entry *head;
+ int p, s, d, o, bcnt;
+ u_int addr;
+
+ o = 0;
+ for (p = 0; p < sizeof(jump_in) / sizeof(jump_in[0]); p++) {
+ bcnt = 0;
+ for (head = jump_in[p]; head != NULL; head = head->next) {
+ tmp_blocks[bcnt].addr = head->vaddr;
+ tmp_blocks[bcnt].regflags = head->reg_sv_flags;
+ bcnt++;
+ }
+ if (bcnt < 1)
+ continue;
+ qsort(tmp_blocks, bcnt, sizeof(tmp_blocks[0]), addr_cmp);
+
+ addr = tmp_blocks[0].addr;
+ for (s = d = 0; s < bcnt; s++) {
+ if (tmp_blocks[s].addr < addr)
+ continue;
+ if (d == 0 || tmp_blocks[d-1].addr != tmp_blocks[s].addr)
+ tmp_blocks[d++] = tmp_blocks[s];
+ addr = scan_for_ret(tmp_blocks[s].addr);
+ }
+
+ if (o + d > maxcount)
+ d = maxcount - o;
+ memcpy(&blocks[o], tmp_blocks, d * sizeof(blocks[0]));
+ o += d;
}
-*/
- //if(Count==365117028) tracedebug=1;
+
+ return o * sizeof(blocks[0]);
+}
+
+void new_dynarec_load_blocks(const void *save, int size)
+{
+ const struct savestate_block *blocks = save;
+ int count = size / sizeof(blocks[0]);
+ u_int regs_save[32];
+ uint32_t f;
+ int i, b;
+
+ get_addr(psxRegs.pc);
+
+ // change GPRs for speculation to at least partially work..
+ memcpy(regs_save, &psxRegs.GPR, sizeof(regs_save));
+ for (i = 1; i < 32; i++)
+ psxRegs.GPR.r[i] = 0x80000000;
+
+ for (b = 0; b < count; b++) {
+ for (f = blocks[b].regflags, i = 0; f; f >>= 1, i++) {
+ if (f & 1)
+ psxRegs.GPR.r[i] = 0x1f800000;
+ }
+
+ get_addr(blocks[b].addr);
+
+ for (f = blocks[b].regflags, i = 0; f; f >>= 1, i++) {
+ if (f & 1)
+ psxRegs.GPR.r[i] = 0x80000000;
+ }
+ }
+
+ memcpy(&psxRegs.GPR, regs_save, sizeof(regs_save));
+}
+
+int new_recompile_block(int addr)
+{
+ u_int pagelimit = 0;
+ u_int state_rflags = 0;
+ int i;
+
assem_debug("NOTCOMPILED: addr = %x -> %x\n", (int)addr, (int)out);
//printf("NOTCOMPILED: addr = %x -> %x\n", (int)addr, (int)out);
//printf("TRACE: count=%d next=%d (compile %x)\n",Count,next_interupt,addr);
@@ -8172,10 +8194,16 @@ int new_recompile_block(int addr)
rlist();
}*/
//rlist();
+
+ // this is just for speculation
+ for (i = 1; i < 32; i++) {
+ if ((psxRegs.GPR.r[i] & 0xffff0000) == 0x1f800000)
+ state_rflags |= 1 << i;
+ }
+
start = (u_int)addr&~3;
//assert(((u_int)addr&1)==0);
new_dynarec_did_compile=1;
-#ifdef PCSX
if (Config.HLE && start == 0x80001000) // hlecall
{
// XXX: is this enough? Maybe check hleSoftCall?
@@ -8189,62 +8217,13 @@ int new_recompile_block(int addr)
#ifdef __arm__
__clear_cache((void *)beginning,out);
#endif
- ll_add(jump_in+page,start,(void *)beginning);
+ ll_add_flags(jump_in+page,start,state_rflags,(void *)beginning);
return 0;
}
- else if ((u_int)addr < 0x00200000 ||
- (0xa0000000 <= addr && addr < 0xa0200000)) {
- // used for BIOS calls mostly?
- source = (u_int *)((u_int)rdram+(start&0x1fffff));
- pagelimit = (addr&0xa0000000)|0x00200000;
- }
- else if (!Config.HLE && (
-/* (0x9fc00000 <= addr && addr < 0x9fc80000) ||*/
- (0xbfc00000 <= addr && addr < 0xbfc80000))) {
- // BIOS
- source = (u_int *)((u_int)psxR+(start&0x7ffff));
- pagelimit = (addr&0xfff00000)|0x80000;
- }
- else
-#endif
-#ifdef MUPEN64
- if ((int)addr >= 0xa4000000 && (int)addr < 0xa4001000) {
- source = (u_int *)((u_int)SP_DMEM+start-0xa4000000);
- pagelimit = 0xa4001000;
- }
- else
-#endif
- if ((int)addr >= 0x80000000 && (int)addr < 0x80000000+RAM_SIZE) {
- source = (u_int *)((u_int)rdram+start-0x80000000);
- pagelimit = 0x80000000+RAM_SIZE;
- }
-#ifndef DISABLE_TLB
- else if ((signed int)addr >= (signed int)0xC0000000) {
- //printf("addr=%x mm=%x\n",(u_int)addr,(memory_map[start>>12]<<2));
- //if(tlb_LUT_r[start>>12])
- //source = (u_int *)(((int)rdram)+(tlb_LUT_r[start>>12]&0xFFFFF000)+(((int)addr)&0xFFF)-0x80000000);
- if((signed int)memory_map[start>>12]>=0) {
- source = (u_int *)((u_int)(start+(memory_map[start>>12]<<2)));
- pagelimit=(start+4096)&0xFFFFF000;
- int map=memory_map[start>>12];
- int i;
- for(i=0;i<5;i++) {
- //printf("start: %x next: %x\n",map,memory_map[pagelimit>>12]);
- if((map&0xBFFFFFFF)==(memory_map[pagelimit>>12]&0xBFFFFFFF)) pagelimit+=4096;
- }
- assem_debug("pagelimit=%x\n",pagelimit);
- assem_debug("mapping=%x (%x)\n",memory_map[start>>12],(memory_map[start>>12]<<2)+start);
- }
- else {
- assem_debug("Compile at unmapped memory address: %x \n", (int)addr);
- //assem_debug("start: %x next: %x\n",memory_map[start>>12],memory_map[(start+4096)>>12]);
- return -1; // Caller will invoke exception handler
- }
- //printf("source= %x\n",(int)source);
- }
-#endif
- else {
- SysPrintf("Compile at bogus memory address: %x \n", (int)addr);
+
+ source = get_source_start(start, &pagelimit);
+ if (source == NULL) {
+ SysPrintf("Compile at bogus memory address: %08x\n", addr);
exit(1);
}
@@ -8259,7 +8238,7 @@ int new_recompile_block(int addr)
/* Pass 9: linker */
/* Pass 10: garbage collection / free memory */
- int i,j;
+ int j;
int done=0;
unsigned int type,op,op2;
@@ -9906,6 +9885,10 @@ int new_recompile_block(int addr)
{
cc+=2; // 2 cycle penalty (after CLOCK_DIVIDER)
}
+ else if(i>1&&itype[i]==STORE&&itype[i-1]==STORE&&itype[i-2]==STORE&&!bt[i])
+ {
+ cc+=4;
+ }
else if(itype[i]==C2LS)
{
cc+=4;
@@ -11569,18 +11552,12 @@ int new_recompile_block(int addr)
u_int page=get_page(vaddr);
u_int vpage=get_vpage(vaddr);
literal_pool(256);
- //if(!(is32[i]&(~unneeded_reg_upper[i])&~(1LL<<CCREG)))
-#ifndef FORCE32
- if(!requires_32bit[i])
-#else
- if(1)
-#endif
{
assem_debug("%8x (%d) <- %8x\n",instr_addr[i],i,start+i*4);
assem_debug("jump_in: %x\n",start+i*4);
ll_add(jump_dirty+vpage,vaddr,(void *)out);
int entry_point=do_dirty_stub(i);
- ll_add(jump_in+page,vaddr,(void *)entry_point);
+ ll_add_flags(jump_in+page,vaddr,state_rflags,(void *)entry_point);
// If there was an existing entry in the hash table,
// replace it with the new address.
// Don't add new entries. We'll insert the
@@ -11593,23 +11570,6 @@ int new_recompile_block(int addr)
ht_bin[3]=entry_point;
}
}
- else
- {
- u_int r=requires_32bit[i]|!!(requires_32bit[i]>>32);
- assem_debug("%8x (%d) <- %8x\n",instr_addr[i],i,start+i*4);
- assem_debug("jump_in: %x (restricted - %x)\n",start+i*4,r);
- //int entry_point=(int)out;
- ////assem_debug("entry_point: %x\n",entry_point);
- //load_regs_entry(i);
- //if(entry_point==(int)out)
- // entry_point=instr_addr[i];
- //else
- // emit_jmp(instr_addr[i]);
- //ll_add_32(jump_in+page,vaddr,r,(void *)entry_point);
- ll_add_32(jump_dirty+vpage,vaddr,r,(void *)out);
- int entry_point=do_dirty_stub(i);
- ll_add_32(jump_in+page,vaddr,r,(void *)entry_point);
- }
}
}
}
diff --git a/libpcsxcore/new_dynarec/new_dynarec.h b/libpcsxcore/new_dynarec/new_dynarec.h
index 1396ef8..ddc84a5 100644
--- a/libpcsxcore/new_dynarec/new_dynarec.h
+++ b/libpcsxcore/new_dynarec/new_dynarec.h
@@ -15,6 +15,8 @@ void new_dynarec_init();
void new_dynarec_cleanup();
void new_dynarec_clear_full();
void new_dyna_start();
+int new_dynarec_save_blocks(void *save, int size);
+void new_dynarec_load_blocks(const void *save, int size);
void invalidate_all_pages();
void invalidate_block(unsigned int block);
diff --git a/libpcsxcore/r3000a.h b/libpcsxcore/r3000a.h
index a6a6254..31aa3f8 100644
--- a/libpcsxcore/r3000a.h
+++ b/libpcsxcore/r3000a.h
@@ -192,9 +192,9 @@ extern psxRegisters psxRegs;
extern u32 event_cycles[PSXINT_COUNT];
extern u32 next_interupt;
-void new_dyna_save(void);
+void new_dyna_before_save(void);
void new_dyna_after_save(void);
-void new_dyna_restore(void);
+void new_dyna_freeze(void *f, int mode);
#define new_dyna_set_event(e, c) { \
s32 c_ = c; \
diff --git a/readme.txt b/readme.txt
index 306cca9..c523f60 100644
--- a/readme.txt
+++ b/readme.txt
@@ -22,7 +22,11 @@ plugin from PCSX4ALL project, and traditional P.E.Op.S. one.
Compiling
---------
-'./configure && make' should work for the most part.
+For libretro build, just doing "make -f Makefile.libretro" is recommended as
+it's the way libretro team is building the core and only Makefile.libretro is
+maintained by them.
+
+For standalone build, './configure && make' should work for the most part.
When compiling for ARM, it's advisable to tell configure script the CPU, FPU
and ABI that matches your target system to get best performance, like this:
@@ -109,6 +113,23 @@ the main menu where it is possible to enable/disable individual cheats.
Changelog
---------
+r20 (2014-12-25)
+* fixed various sound accuracy issues, like effects in ff7-ff9
+ for standalone build, audio will no longer slow down when emu is not fast
+ enough and stutter instead, as the former behavior causes accuracy issues.
+ Old mode can be restored in SPU plugin config options, but is not recommended.
+* savestates now save small parts of dynarec state to reduce dynarec related
+ slowdowns after savestate load
+* menu: fixed file browser issues with filesystems like exfat-fuse
+* menu: memcard manager: selected card is saved in config now
+* standalone: added some basic scanline efect
+* some CD image loading fixes
+* converted asm code to be compatible with more assemblers, like Apple's gas
++ libretro: added Makefile.libretro and support for various platforms like
+ iOS and QNX. Makefile.libretro is recommended way to do libretro builds
+ (patches from CatalystG, squarepusher, notaz and others, see git).
+* some other minor fixes
+
r19 (2013-03-17)
+ libretro: added region, multidisk support
* more work on cdrom code