Browse Source

Merge pull request #8 from DrUm78/FunKey

Last irixxxx's optimizations for 32X and Sega-CD + volume fix
Vincent-FK 2 years ago
parent
commit
99a45bbe18
54 changed files with 2624 additions and 2411 deletions
  1. 5 2
      .gitmodules
  2. 2 1
      Makefile
  3. 21 4
      Makefile.libretro
  4. 1 1
      cpu/cyclone
  5. 1 1
      cpu/drc/emit_arm64.c
  6. 18 11
      cpu/drc/emit_ppc.c
  7. 3 6
      cpu/drc/emit_x86.c
  8. 2 5
      cpu/fame/famec_opcodes.h
  9. 1 1
      cpu/musashi/m68k_in.c
  10. 5 5
      cpu/musashi/m68kcpu.c
  11. 7 8
      cpu/musashi/m68kcpu.h
  12. 3 0
      cpu/musashi/m68kmake.c
  13. 93 46
      cpu/sh2/compiler.c
  14. 4 3
      cpu/sh2/compiler.h
  15. 7 5
      cpu/sh2/mame/sh2pico.c
  16. 2 2
      cpu/sh2/sh2.h
  17. 10 10
      pico/32x/32x.c
  18. 1 1
      pico/32x/draw.c
  19. 52 17
      pico/32x/draw_arm.S
  20. 27 22
      pico/32x/memory.c
  21. 4 0
      pico/32x/sh2soc.c
  22. 9 7
      pico/cart.c
  23. 7 7
      pico/cd/cdc.c
  24. 3 0
      pico/cd/cdd.c
  25. 1 1
      pico/cd/gfx.c
  26. 1 1
      pico/cd/libchdr
  27. 41 19
      pico/cd/mcd.c
  28. 21 13
      pico/cd/memory.c
  29. 8 8
      pico/cd/pcm.c
  30. 2 2
      pico/m68kif_cyclone.s
  31. 0 176
      pico/misc.c
  32. 5 5
      pico/patch.c
  33. 1 0
      pico/pico.c
  34. 2 1
      pico/pico_cmn.c
  35. 8 5
      pico/pico_int.h
  36. 2 2
      pico/pico_port.h
  37. 21 21
      pico/sek.c
  38. 2 2
      pico/sound/mix_arm.S
  39. 3 3
      pico/sound/sound.c
  40. 2 0
      pico/sound/ym2612.c
  41. 150 44
      pico/videoport.c
  42. 1 1
      platform/common/config_file.c
  43. 1 0
      platform/common/dr_libs
  44. 1922 1922
      platform/common/emu.c
  45. 27 2
      platform/common/host_dasm.c
  46. 1 1
      platform/common/minimp3
  47. 65 0
      platform/common/mp3_drmp3.c
  48. 1 1
      platform/common/plat_sdl.c
  49. 21 5
      platform/libretro/libretro.c
  50. 1 1
      platform/linux/emu.c
  51. 16 0
      platform/linux/menu.c
  52. 1 1
      platform/opendingux/data/default.gcw0.desktop
  53. 2 2
      platform/opendingux/data/default.retrofw.desktop
  54. 7 7
      tools/release.sh

+ 5 - 2
.gitmodules

@@ -3,13 +3,16 @@
 	url = https://github.com/FunKey-Project/picofe-irixxxx.git
 [submodule "cpu/cyclone"]
 	path = cpu/cyclone
-	url = https://github.com/notaz/cyclone68000.git
+	url = https://github.com/irixxxx/cyclone68000.git
 [submodule "pico/sound/emu2413"]
 	path = pico/sound/emu2413
 	url = https://github.com/digital-sound-antiques/emu2413.git
 [submodule "pico/cd/libchdr"]
 	path = pico/cd/libchdr
-	url = https://github.com/irixxxx/libchdr-picodrive.git
+	url = https://github.com/rtissera/libchdr.git
 [submodule "platform/common/minimp3"]
 	path = platform/common/minimp3
 	url = https://github.com/lieff/minimp3
+[submodule "platform/common/dr_libs"]
+	path = platform/common/dr_libs
+	url = https://github.com/mackron/dr_libs

+ 2 - 1
Makefile

@@ -245,7 +245,8 @@ OBJS += platform/common/mp3_helix.o
 else ifeq "$(HAVE_LIBAVCODEC)" "1"
 OBJS += platform/common/mp3_libavcodec.o
 else
-OBJS += platform/common/mp3_minimp3.o
+#OBJS += platform/common/mp3_minimp3.o
+OBJS += platform/common/mp3_drmp3.o
 endif
 endif
 

+ 21 - 4
Makefile.libretro

@@ -160,6 +160,9 @@ else ifeq ($(platform), ps3)
 	CFLAGS += -DBLARGG_BIG_ENDIAN=1 -D__ppc__ -DFAMEC_NO_GOTOS
 	STATIC_LINKING = 1
 	NO_MMAP = 1
+	# PS3 has memory mapped in a way not suitable for DRC
+	use_sh2drc = 0
+	use_svpdrc = 0
 
 # sncps3
 else ifeq ($(platform), sncps3)
@@ -170,6 +173,9 @@ else ifeq ($(platform), sncps3)
 	CFLAGS += -DBLARGG_BIG_ENDIAN=1 -D__ppc__ -DFAMEC_NO_GOTOS
 	STATIC_LINKING = 1
 	NO_MMAP = 1
+	# PS3 has memory mapped in a way not suitable for DRC
+	use_sh2drc = 0
+	use_svpdrc = 0
 
 # Lightweight PS3 Homebrew SDK
 else ifeq ($(platform), psl1ght)
@@ -179,6 +185,9 @@ else ifeq ($(platform), psl1ght)
 	CFLAGS += -DBLARGG_BIG_ENDIAN=1 -D__ppc__ -DFAMEC_NO_GOTOS
 	STATIC_LINKING = 1
 	NO_MMAP = 1
+	# PS3 has memory mapped in a way not suitable for DRC
+	use_sh2drc = 0
+	use_svpdrc = 0
 
 # PSP
 else ifeq ($(platform), psp1)
@@ -253,14 +262,18 @@ else ifeq ($(platform), ngc)
 	TARGET := $(TARGET_NAME)_libretro_$(platform).a
 	CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
 	AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)
-	CFLAGS += -DGEKKO -DHW_DOL -mrvl -mcpu=750 -meabi -mhard-float -DBLARGG_BIG_ENDIAN=1 -D__ppc__
+	CFLAGS += -DGEKKO -DHW_DOL -mrvl -mcpu=750 -meabi -mhard-float -D__ppc__ -DMSB_FIRST
+	STATIC_LINKING = 1
+	NO_MMAP = 1
 
 # Nintendo Wii
 else ifeq ($(platform), wii)
 	TARGET := $(TARGET_NAME)_libretro_$(platform).a
 	CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
 	AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)
-	CFLAGS += -DGEKKO -DHW_RVL -mrvl -mcpu=750 -meabi -mhard-float -DBLARGG_BIG_ENDIAN=1 -D__ppc__
+	CFLAGS += -DGEKKO -DHW_RVL -mrvl -mcpu=750 -meabi -mhard-float -D__ppc__ -DMSB_FIRST
+	STATIC_LINKING = 1
+	NO_MMAP = 1
 
 # Nintendo Wii U
 else ifeq ($(platform), wiiu)
@@ -268,8 +281,12 @@ else ifeq ($(platform), wiiu)
 	CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
 	CXX = $(DEVKITPPC)/bin/powerpc-eabi-g++$(EXE_EXT)
 	AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)
-	COMMONFLAGS += -DGEKKO -DWIIU -DHW_RVL -mwup -mcpu=750 -meabi -mhard-float -D__POWERPC__ -D__ppc__ -DMSB_FIRST -DWORDS_BIGENDIAN=1 -I./deps/include/
-	COMMONFLAGS += -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int
+	CFLAGS += -DGEKKO -DWIIU -DHW_RVL -mwup -mcpu=750 -meabi -mhard-float -D__ppc__ -DMSB_FIRST
+	STATIC_LINKING = 1
+	NO_MMAP = 1
+	# Wii U has memory mapped in a way not suitable for DRC
+	use_sh2drc = 0
+	use_svpdrc = 0
 
 # Nintendo Switch (libtransistor)
 else ifeq ($(platform), switch)

+ 1 - 1
cpu/cyclone

@@ -1 +1 @@
-Subproject commit 94a9d9a888e72cbfa4db12113cd113cf5e154f7f
+Subproject commit 1f03ed6a49252bfb900c4d3932094308e1c5b375

+ 1 - 1
cpu/drc/emit_arm64.c

@@ -1187,7 +1187,7 @@ static void emith_ldst_offs(int sz, int rd, int rn, int o9, int ld, int mode)
 #define host_instructions_updated(base, end, force) \
 	do { if (force) __builtin___clear_cache(base, end); } while (0)
 #define	emith_update_cache()	/**/
-#define emith_rw_offs_max()	0xff
+#define emith_rw_offs_max()	0x1ff
 #define emith_uext_ptr(r)	/**/
 
 

+ 18 - 11
cpu/drc/emit_ppc.c

@@ -362,6 +362,11 @@ enum { OPS_STD, OPS_STDU /*,OPS_STQ*/ };
 #define	PPC_STB_IMM(rt, ra, offs16) \
 	PPC_OP_IMM(OP_STB,rt,ra,(u16)(offs16))
 
+#define	PPC_STXU_IMM(rt, ra, offs16) \
+	PPC_OP_IMM(OP__ST,rt,ra,((u16)(offs16)&~3)|OPS_STDU)
+#define	PPC_STWU_IMM(rt, ra, offs16) \
+	PPC_OP_IMM(OP_STWU,rt,ra,(u16)(offs16))
+
 // load/store, indexed
 
 #define PPC_LDX_REG(rt, ra, rb) \
@@ -407,6 +412,7 @@ enum { OPS_STD, OPS_STDU /*,OPS_STQ*/ };
 #define PPC_LDP_REG			PPC_LDX_REG
 #define PPC_STP_IMM			PPC_STX_IMM
 #define PPC_STP_REG			PPC_STX_REG
+#define PPC_STPU_IMM			PPC_STXU_IMM
 #define PPC_BFXP_IMM			PPC_BFX_IMM
 
 #define emith_uext_ptr(r)		EMIT(PPC_EXTUW_REG(r, r))
@@ -440,6 +446,7 @@ enum { OPS_STD, OPS_STDU /*,OPS_STQ*/ };
 #define PPC_LDP_REG			PPC_LDW_REG
 #define PPC_STP_IMM			PPC_STW_IMM
 #define PPC_STP_REG			PPC_STW_REG
+#define PPC_STPU_IMM			PPC_STWU_IMM
 #define PPC_BFXP_IMM			PPC_BFXW_IMM
 
 #define emith_uext_ptr(r)		/**/
@@ -1552,10 +1559,11 @@ static int emith_cond_check(int cond)
 static NOINLINE void host_instructions_updated(void *base, void *end, int force)
 {
 	int step = 32, lgstep = 5;
-	char *_base = base, *_end = end;
-	int count = (_end - _base + step-1) >> lgstep;
+	char *_base = (char *)((uptr)base & ~(step-1));
+	int count = (((char *)end - _base) >> lgstep) + 1;
 
 	if (count <= 0) count = 1;	// make sure count is positive
+	base = _base;
 
 	asm volatile(
 	"	mtctr	%1;"
@@ -1587,25 +1595,24 @@ static NOINLINE void host_instructions_updated(void *base, void *end, int force)
 #define emith_sh2_drc_entry() do { \
 	int _c, _z = PTR_SIZE; u32 _m = 0xffffc000; /* r14-r31 */ \
 	if (__builtin_parity(_m) == 1) _m |= 0x1; /* ABI align for SP is 16 */ \
-	int _s = count_bits(_m) * _z, _o = 0; \
-	for (_c = HOST_REGS-1; _m && _c >= 0; _m &= ~(1 << _c), _c--) \
+	int _s = count_bits(_m) * _z, _o = STACK_EXTRA; \
+	EMIT(PPC_STPU_IMM(SP, SP, -_s-STACK_EXTRA)); \
+	EMIT(PPC_MFSP_REG(AT, LR)); \
+	for (_c = 0; _m && _c < HOST_REGS; _m &= ~(1 << _c), _c++) \
 		if (_m & (1 << _c)) \
-			{ _o -= _z; if (_c) emith_write_r_r_offs_ptr(_c, SP, _o); } \
-	EMIT(PPC_MFSP_REG(10, LR)); \
-	emith_write_r_r_offs_ptr(10, SP, 2*PTR_SIZE); \
-	emith_write_r_r_offs_ptr(SP, SP, -_s-STACK_EXTRA); /* XXX stdu */ \
-	emith_add_r_r_ptr_imm(SP, SP, -_s-STACK_EXTRA); \
+			{ if (_c) emith_write_r_r_offs_ptr(_c, SP, _o); _o += _z; } \
+	emith_write_r_r_offs_ptr(AT, SP, _o + _z); \
 } while (0)
 #define emith_sh2_drc_exit() do { \
 	int _c, _z = PTR_SIZE; u32 _m = 0xffffc000; \
 	if (__builtin_parity(_m) == 1) _m |= 0x1; \
 	int _s = count_bits(_m) * _z, _o = STACK_EXTRA; \
+	emith_read_r_r_offs_ptr(AT, SP, _o+_s + _z); \
+	EMIT(PPC_MTSP_REG(AT, LR)); \
 	for (_c = 0; _m && _c < HOST_REGS; _m &= ~(1 << _c), _c++) \
 		if (_m & (1 << _c)) \
 			{ if (_c) emith_read_r_r_offs_ptr(_c, SP, _o); _o += _z; } \
 	emith_add_r_r_ptr_imm(SP, SP, _s+STACK_EXTRA); \
-	emith_read_r_r_offs_ptr(10, SP, 2*PTR_SIZE); \
-	EMIT(PPC_MTSP_REG(10, LR)); \
 	emith_ret(); \
 } while (0)
 

+ 3 - 6
cpu/drc/emit_x86.c

@@ -383,12 +383,9 @@ enum { xAX = 0, xCX, xDX, xBX, xSP, xBP, xSI, xDI,	// x86-64,i386 common
 
 // _r_imm
 #define emith_move_r_imm(r, imm) do { \
-	if (imm) { \
-		EMIT_REX_IF(0, 0, r); \
-		EMIT_OP(0xb8 + ((r)&7)); \
-		EMIT(imm, u32); \
-	} else \
-		emith_eor_r_r(r, r); \
+	EMIT_REX_IF(0, 0, r); \
+	EMIT_OP(0xb8 + ((r)&7)); \
+	EMIT(imm, u32); \
 } while (0)
 
 #define emith_move_r_ptr_imm(r, imm) do { \

+ 2 - 5
cpu/fame/famec_opcodes.h

@@ -4614,7 +4614,7 @@ OPCODE(0x013C)
 	src = 1 << (src & 7);
 	FETCH_BYTE(res);
 	flag_NotZ = res & src;
-RET(8)
+RET(10)
 }
 
 // BTST
@@ -19318,6 +19318,7 @@ OPCODE(0x4E73)
 		AREG(7) = ASP;
 		ASP = res;
 	}
+	CHECK_BRANCH_EXCEPTION(res)
 	POST_IO
 	ctx->execinfo &= ~(FM68K_EMULATE_GROUP_0|FM68K_EMULATE_TRACE|FM68K_DO_TRACE);
 	CHECK_INT_TO_JUMP(20)
@@ -24027,11 +24028,7 @@ OPCODE(0x5048)
 	dst = AREGu32((Opcode >> 0) & 7);
 	res = dst + src;
 	AREG((Opcode >> 0) & 7) = res;
-#ifdef USE_CYCLONE_TIMING
-RET(4)
-#else
 RET(8)
-#endif
 }
 
 // ADDQ

+ 1 - 1
cpu/musashi/m68k_in.c

@@ -389,7 +389,7 @@ addi      32  .     .     0000011010......  A+-DXWL...  U U U U  20  20   4   4
 addq       8  .     d     0101...000000...  ..........  U U U U   4   4   2   2
 addq       8  .     .     0101...000......  A+-DXWL...  U U U U   8   8   4   4
 addq      16  .     d     0101...001000...  ..........  U U U U   4   4   2   2
-addq      16  .     a     0101...001001...  ..........  U U U U   4   4   2   2
+addq      16  .     a     0101...001001...  ..........  U U U U   8   8   2   2
 addq      16  .     .     0101...001......  A+-DXWL...  U U U U   8   8   4   4
 addq      32  .     d     0101...010000...  ..........  U U U U   8   8   2   2
 addq      32  .     a     0101...010001...  ..........  U U U U   8   8   2   2

+ 5 - 5
cpu/musashi/m68kcpu.c

@@ -43,7 +43,7 @@ extern void m68040_fpu_op1(void);
 /* ================================= DATA ================================= */
 /* ======================================================================== */
 
-int  m68ki_initial_cycles;
+//int  m68ki_initial_cycles;
 //int  m68ki_remaining_cycles = 0;                     /* Number of clocks remaining */
 uint m68ki_tracing = 0;
 uint m68ki_address_space;
@@ -828,6 +828,10 @@ int m68k_execute(int num_cycles)
 			m68ki_instruction_jump_table[REG_IR]();
 			USE_CYCLES(CYC_INSTRUCTION[REG_IR]); // moving this up may cause a deadlock
 
+			/* ASG: update cycles */
+			USE_CYCLES(CPU_INT_CYCLES);
+			CPU_INT_CYCLES = 0;
+
 			/* Trace m68k_exception, if necessary */
 			m68ki_exception_if_trace(); /* auto-disable (see m68kcpu.h) */
 
@@ -839,10 +843,6 @@ int m68k_execute(int num_cycles)
 		/* set previous PC to current PC for the next entry into the loop */
 		REG_PPC = REG_PC;
 
-		/* ASG: update cycles */
-		USE_CYCLES(CPU_INT_CYCLES);
-		CPU_INT_CYCLES = 0;
-
 		/* return how many clocks we used */
 		return m68ki_initial_cycles - GET_CYCLES();
 	}

+ 7 - 8
cpu/musashi/m68kcpu.h

@@ -27,14 +27,11 @@
 #define M68KCPU__HEADER
 
 // notaz: something's missing this
-#ifndef UINT64
-#define UINT64 unsigned long long
-#endif
-#ifndef UINT16
-#define UINT32 unsigned int
-#define UINT16 unsigned short
-#define UINT8  unsigned char
-#endif
+#include <stdint.h>
+typedef uint64_t UINT64;
+typedef uint32_t UINT32;
+typedef uint16_t UINT16;
+typedef uint8_t  UINT8;
 
 #include "m68k.h"
 #include <limits.h>
@@ -919,6 +916,7 @@ typedef struct
 	void (*instr_hook_callback)(void);                /* Called every instruction cycle prior to execution */
 
 	// notaz
+	sint cyc_initial_cycles;
 	sint cyc_remaining_cycles;
 	sint not_polling;
 } m68ki_cpu_core;
@@ -926,6 +924,7 @@ typedef struct
 // notaz
 extern m68ki_cpu_core *m68ki_cpu_p;
 #define m68ki_cpu (*m68ki_cpu_p)
+#define m68ki_initial_cycles m68ki_cpu_p->cyc_initial_cycles
 #define m68ki_remaining_cycles m68ki_cpu_p->cyc_remaining_cycles
 
 

+ 3 - 0
cpu/musashi/m68kmake.c

@@ -641,6 +641,9 @@ int get_oper_cycles(opcode_struct* op, int ea_mode, int cpu_type)
 			strcmp(op->name, "suba")   == 0))
 			return op->cycles[cpu_type] + g_ea_cycle_table[ea_mode][cpu_type][size] + 2;
 
+		if(cpu_type == CPU_TYPE_000 && ea_mode == EA_MODE_I && op->size == 8 && strcmp(op->name, "btst") == 0)
+			return op->cycles[cpu_type] + g_ea_cycle_table[ea_mode][cpu_type][size] + 2;
+
 		if(strcmp(op->name, "jmp") == 0)
 			return op->cycles[cpu_type] + g_jmp_cycle_table[ea_mode];
 		if(strcmp(op->name, "jsr") == 0)

+ 93 - 46
cpu/sh2/compiler.c

@@ -203,19 +203,35 @@ static char sh2dasm_buff[64];
 		(ulong)(sh2)->poll_addr, (sh2)->poll_cycles, (sh2)->poll_cnt); \
 }
 
-#if (DRC_DEBUG & (8|256|512|1024)) || defined(PDB)
 #if (DRC_DEBUG & (256|512|1024))
 static SH2 csh2[2][8];
 static FILE *trace[2];
 static int topen[2];
 #endif
+#if (DRC_DEBUG & 8)
+static u32 lastpc, lastcnt;
+static void *lastblock;
+#endif
+#if (DRC_DEBUG & (8|256|512|1024)) || defined(PDB)
 static void REGPARM(3) *sh2_drc_log_entry(void *block, SH2 *sh2, u32 sr)
 {
   if (block != NULL) {
-    dbg(8, "= %csh2 enter %08x %p, c=%d", sh2->is_slave ? 's' : 'm',
-      sh2->pc, block, (signed int)sr >> 12);
 #if defined PDB
+    dbg(8, "= %csh2 enter %08x %p, c=%d", sh2->is_slave?'s':'m',
+      sh2->pc, block, (signed int)sr >> 12);
     pdb_step(sh2, sh2->pc);
+#elif (DRC_DEBUG & 8)
+    if (lastpc != sh2->pc) {
+      if (lastcnt)
+        dbg(8, "= %csh2 enter %08x %p (%d times), c=%d", sh2->is_slave?'s':'m',
+          lastpc, lastblock, lastcnt, (signed int)sr >> 12);
+      dbg(8, "= %csh2 enter %08x %p, c=%d", sh2->is_slave?'s':'m',
+        sh2->pc, block, (signed int)sr >> 12);
+      lastpc = sh2->pc;
+      lastblock = block;
+      lastcnt = 0;
+    } else
+      lastcnt++;
 #elif (DRC_DEBUG & 256)
   {
     static SH2 fsh2;
@@ -468,7 +484,7 @@ static void rcache_free_tmp(int hr);
 #include "../drc/emit_mips.c"
 #elif defined(__riscv__) || defined(__riscv)
 #include "../drc/emit_riscv.c"
-#elif defined(__powerpc__) || defined(_M_PPC)
+#elif defined(__powerpc__) || defined(__ppc__) || defined(_M_PPC)
 #include "../drc/emit_ppc.c"
 #elif defined(__i386__) || defined(_M_X86)
 #include "../drc/emit_x86.c"
@@ -747,6 +763,7 @@ static void rm_from_hashlist(struct block_entry *be, int tcache_id)
 }
 
 
+#if LINK_BRANCHES
 static void add_to_hashlist_unresolved(struct block_link *bl, int tcache_id)
 {
   u32 tcmask = HASH_TABLE_SIZE(tcache_id) - 1;
@@ -789,7 +806,6 @@ static void rm_from_hashlist_unresolved(struct block_link *bl, int tcache_id)
     bl->next->prev = bl->prev;
 }
 
-#if LINK_BRANCHES
 static void dr_block_link(struct block_entry *be, struct block_link *bl, int emit_jump)
 {
   dbg(2, "- %slink from %p to pc %08x entry %p", emit_jump ? "":"early ",
@@ -1017,23 +1033,23 @@ static void dr_rm_block_entry(struct block_desc *bd, int tcache_id, u32 nolit, i
     return;
   }
 
-#if LINK_BRANCHES
   // remove from hash table, make incoming links unresolved
   if (bd->active) {
     for (i = 0; i < bd->entry_count; i++) {
       rm_from_hashlist(&bd->entryp[i], tcache_id);
 
+#if LINK_BRANCHES
       while ((bl = bd->entryp[i].links) != NULL) {
         dr_block_unlink(bl, 1);
         add_to_hashlist_unresolved(bl, tcache_id);
       }
+#endif
     }
 
     dr_mark_memory(-1, bd, tcache_id, nolit);
     add_to_block_list(&inactive_blocks[tcache_id], bd);
   }
   bd->active = 0;
-#endif
 
   if (free) {
 #if LINK_BRANCHES
@@ -1053,7 +1069,6 @@ static void dr_rm_block_entry(struct block_desc *bd, int tcache_id, u32 nolit, i
     rm_from_block_lists(bd);
     bd->addr = bd->size = bd->addr_lit = bd->size_lit = 0;
     bd->entry_count = 0;
-    bd->entryp = NULL;
   }
   emith_update_cache();
 }
@@ -1339,6 +1354,7 @@ static void rcache_add_vreg_alias(int x, sh2_reg_e r);
 static void rcache_remove_vreg_alias(int x, sh2_reg_e r);
 static void rcache_evict_vreg(int x);
 static void rcache_remap_vreg(int x);
+static int rcache_get_reg(sh2_reg_e r, rc_gr_mode mode, int *hr);
 
 static void rcache_set_x16(int hr, int s16_, int u16_)
 {
@@ -1444,7 +1460,6 @@ static int rcache_is_u16(int hr)
   } */ \
 }
 
-#if PROPAGATE_CONSTANTS
 static inline int gconst_alloc(sh2_reg_e r)
 {
   int i, n = -1;
@@ -1481,7 +1496,6 @@ static void gconst_new(sh2_reg_e r, u32 val)
   if (guest_regs[r].vreg >= 0)
     rcache_remove_vreg_alias(guest_regs[r].vreg, r);
 }
-#endif
 
 static int gconst_get(sh2_reg_e r, u32 *val)
 {
@@ -2598,8 +2612,8 @@ static uptr split_address(uptr la, uptr mask, s32 *offs)
   uptr sign = (mask>>1) + 1; // sign bit in offset
   *offs = (la & mask) | (la & sign ? ~mask : 0); // offset part, sign extended
   la = (la & ~mask) + ((la & sign) << 1); // base part, corrected for offs sign
-  if (~mask && la == ~mask && !(*offs & sign)) { // special case la=-1 & offs>0
-    *offs = -*offs;
+  if (~mask && la == ~mask && *offs > 0) { // special case la=-1&~mask && offs>0
+    *offs -= mask+1;
     la = 0;
   }
   return la;
@@ -2661,6 +2675,11 @@ static int emit_get_rbase_and_offs(SH2 *sh2, sh2_reg_e r, int rmode, s32 *offs)
   } else {
     // known fixed host address
     la = split_address(la + ((a + *offs) & mask), omask, offs);
+    if (la == 0) {
+      // offset only. optimize for hosts having short indexed addressing
+      la = *offs & ~0x7f;  // keep the lower bits for endianess handling
+      *offs &= 0x7f;
+    }
     hr = rcache_get_tmp();
     emith_move_r_ptr_imm(hr, la);
   }
@@ -3014,12 +3033,22 @@ static void emit_do_static_regs(int is_write, int tmpr)
 // divide operation replacement functions, called by compiled code. Only the
 // 32:16 cases and the 64:32 cases described in the SH2 prog man are replaced.
 
-static uint32_t REGPARM(2) sh2_drc_divu32(uint32_t dv, uint32_t ds)
+// This is surprisingly difficult since the SH2 division operation is generating
+// the result in the dividend during the operation, leaving some remainder-like
+// stuff in the bits unused for the result, and leaving the T and Q status bits
+// in a state depending on the operands and the result. Q always reflects the
+// last result bit generated (i.e. bit 0 of the result). For T:
+//	32:16	T = top bit of the 16 bit remainder-like
+//	64:32	T = resulting T of the DIV0U/S operation
+// The remainder-like depends on outcome of the last generated result bit.
+
+static uint32_t REGPARM(3) sh2_drc_divu32(uint32_t dv, uint32_t *dt, uint32_t ds)
 {
-  if (ds && ds >= dv) {
-    // good case: no divide by 0, and no result overflow
+  if (ds > dv && (uint16_t)ds == 0) {
+    // good case: no overflow, divisor not 0, lower 16 bits 0
     uint32_t quot = dv / (ds>>16), rem = dv - (quot * (ds>>16));
     if (~quot&1) rem -= ds>>16;
+    *dt = (rem>>15) & 1;
     return (uint16_t)quot | ((2*rem + (quot>>31)) << 16);
   } else {
     // bad case: use the sh2 algo to get the right result
@@ -3029,20 +3058,21 @@ static uint32_t REGPARM(2) sh2_drc_divu32(uint32_t dv, uint32_t ds)
       dv = (dv<<1) | t;
       t = v;
       v = dv;
-      if (q)  dv += ds, q =   dv < v;
-      else    dv -= ds, q = !(dv < v);
+      if (q)  dv += ds, q = dv < v;
+      else    dv -= ds, q = dv > v;
       q ^= t, t = !q;
     }
+    *dt = dv>>31;
     return (dv<<1) | t;
   }
 }
 
 static uint32_t REGPARM(3) sh2_drc_divu64(uint32_t dh, uint32_t *dl, uint32_t ds)
 {
-  if (ds > 1 && ds >= dh) {
-    // good case: no divide by 0, and no result overflow
+  if (ds > dh) {
+    // good case: no overflow, divisor not 0
     uint64_t dv = *dl | ((uint64_t)dh << 32);
-    uint32_t quot = dv / ds, rem = dv - (quot * ds);
+    uint32_t quot = dv / ds, rem = dv - ((uint64_t)quot * ds);
     if (~quot&1) rem -= ds;
     *dl = quot;
     return rem;
@@ -3055,8 +3085,8 @@ static uint32_t REGPARM(3) sh2_drc_divu64(uint32_t dh, uint32_t *dl, uint32_t ds
       dv = (dv<<1) | t;
       t = v;
       v = dv;
-      if (q)  dv += ((uint64_t)ds << 32), q =   dv < v;
-      else    dv -= ((uint64_t)ds << 32), q = !(dv < v);
+      if (q)  dv += ((uint64_t)ds << 32), q = dv < v;
+      else    dv -= ((uint64_t)ds << 32), q = dv > v;
       q ^= t, t = !q;
     }
     *dl = (dv<<1) | t;
@@ -3064,16 +3094,17 @@ static uint32_t REGPARM(3) sh2_drc_divu64(uint32_t dh, uint32_t *dl, uint32_t ds
   }
 }
 
-static uint32_t REGPARM(2) sh2_drc_divs32(int32_t dv, int32_t ds)
+static uint32_t REGPARM(3) sh2_drc_divs32(int32_t dv, uint32_t *dt, int32_t ds)
 {
   uint32_t adv = abs(dv), ads = abs(ds)>>16;
-  if (ads > 1 && ads > adv>>16 && (int32_t)ads > 0 && !(uint16_t)ds) {
-    // good case: no divide by 0, and no result overflow
+  if (ads > adv>>16 && ds != 0x80000000 && (int16_t)ds == 0) {
+    // good case: no overflow, divisor not 0 and not MIN_INT, lower 16 bits 0
     uint32_t quot = adv / ads, rem = adv - (quot * ads);
     int m1 = (rem ? dv^ds : ds) < 0;
     if (rem && dv < 0)  rem = (quot&1 ? -rem : +ads-rem);
     else                rem = (quot&1 ? +rem : -ads+rem);
     quot = ((dv^ds)<0 ? -quot : +quot) - m1;
+    *dt = (rem>>15) & 1;
     return (uint16_t)quot | ((2*rem + (quot>>31)) << 16);
   } else {
     // bad case: use the sh2 algo to get the right result
@@ -3083,10 +3114,11 @@ static uint32_t REGPARM(2) sh2_drc_divs32(int32_t dv, int32_t ds)
       dv = (dv<<1) | t;
       t = v;
       v = dv;
-      if (m^q)  dv += ds, q =   (uint32_t)dv < v;
-      else      dv -= ds, q = !((uint32_t)dv < v);
+      if (m^q)  dv += ds, q = (uint32_t)dv < v;
+      else      dv -= ds, q = (uint32_t)dv > v;
       q ^= m^t, t = !(m^q);
     }
+    *dt = (uint32_t)dv>>31;
     return (dv<<1) | t;
   }
 }
@@ -3096,8 +3128,8 @@ static uint32_t REGPARM(3) sh2_drc_divs64(int32_t dh, uint32_t *dl, int32_t ds)
   int64_t _dv = *dl | ((int64_t)dh << 32);
   uint64_t adv = (_dv < 0 ? -_dv : _dv); // llabs isn't in older toolchains
   uint32_t ads = abs(ds);
-  if (ads > 1 && ads > adv>>32 && (int64_t)adv > 0) {
-    // good case: no divide by 0, and no result overflow
+  if (ads > adv>>32 && ds != 0x80000000) {
+    // good case: no overflow, divisor not 0 and not MIN_INT
     uint32_t quot = adv / ads, rem = adv - ((uint64_t)quot * ads);
     int m1 = (rem ? dh^ds : ds) < 0;
     if (rem && dh < 0) rem = (quot&1 ? -rem : +ads-rem);
@@ -3110,12 +3142,12 @@ static uint32_t REGPARM(3) sh2_drc_divs64(int32_t dh, uint32_t *dl, int32_t ds)
     uint64_t dv = *dl | ((uint64_t)dh << 32);
     int m = (uint32_t)ds>>31, q = (uint64_t)dv>>63, t = m^q, s = 32;
     while (s--) {
-      int64_t v = (uint64_t)dv>>63;
+      uint64_t v = (uint64_t)dv>>63;
       dv = (dv<<1) | t;
       t = v;
       v = dv;
-      if (m^q)  dv += ((uint64_t)ds << 32), q =   dv < v;
-      else      dv -= ((uint64_t)ds << 32), q = !(dv < v);
+      if (m^q)  dv += ((uint64_t)ds << 32), q = dv < v;
+      else      dv -= ((uint64_t)ds << 32), q = dv > v;
       q ^= m^t, t = !(m^q);
     }
     *dl = (dv<<1) | t;
@@ -3508,7 +3540,7 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
         // if exiting a pinned loop pinned regs must be written back to ctx
         // since they are reloaded in the loop entry code
         emith_cmp_r_imm(sr, 0);
-        EMITH_JMP_START(DCOND_GT);
+        EMITH_JMP_START(DCOND_GE);
         rcache_save_pinned();
 
         if (blx_target_count < ARRAY_SIZE(blx_targets)) {
@@ -3532,13 +3564,13 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
           emith_cmp_r_imm(sr, 0);
           blx_targets[blx_target_count++] =
               (struct linkage) { .pc = pc, .ptr = tcache_ptr, .mask = 0x1 };
-          emith_jump_cond_patchable(DCOND_LE, tcache_ptr);
+          emith_jump_cond_patchable(DCOND_LT, tcache_ptr);
         } else {
           // blx table full, must inline exit code
           tmp = rcache_get_tmp_arg(0);
           emith_cmp_r_imm(sr, 0);
           EMITH_SJMP_START(DCOND_GT);
-          emith_move_r_imm_c(DCOND_LE, tmp, pc);
+          emith_move_r_imm_c(DCOND_LT, tmp, pc);
           emith_jump_cond(DCOND_LE, sh2_drc_exit);
           EMITH_SJMP_END(DCOND_GT);
           rcache_free_tmp(tmp);
@@ -3921,8 +3953,10 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
 #if DIV_OPTIMIZER
           if (div(opd).div1 == 16 && div(opd).ro == div(opd).rn) {
             // divide 32/16
+            tmp = rcache_get_tmp_arg(1);
+            emith_add_r_r_ptr_imm(tmp, CONTEXT_REG, offsetof(SH2, drc_tmp));
             rcache_get_reg_arg(0, div(opd).rn, NULL);
-            rcache_get_reg_arg(1, div(opd).rm, NULL);
+            rcache_get_reg_arg(2, div(opd).rm, NULL);
             rcache_invalidate_tmp();
             emith_abicall(sh2_drc_divu32);
             tmp = rcache_get_tmp_ret();
@@ -3934,8 +3968,9 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
             emith_and_r_r_imm(tmp3, tmp2, 1);     // Q = !Rn[0]
             emith_eor_r_r_imm(tmp3, tmp3, 1);
             emith_or_r_r_lsl(sr, tmp3, Q_SHIFT);
+            emith_ctx_read(tmp3, offsetof(SH2, drc_tmp));
+            emith_or_r_r_r(sr, sr, tmp3);         // T
             rcache_free_tmp(tmp3);
-            emith_or_r_r_r_lsr(sr, sr, tmp2, 31); // T = Rn[31]
             skip_op = div(opd).div1 + div(opd).rotcl;
           }
           else if (div(opd).div1 == 32 && div(opd).ro != div(opd).rn) {
@@ -3960,7 +3995,6 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
             emith_eor_r_r_imm(tmp3, tmp3, 1);
             emith_or_r_r_lsl(sr, tmp3, Q_SHIFT);
             rcache_free_tmp(tmp3);
-            emith_or_r_r_r_lsr(sr, sr, tmp4, 31); // T = Ro[31]
             skip_op = div(opd).div1 + div(opd).rotcl;
           }
 #endif
@@ -4035,8 +4069,10 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
 #if DIV_OPTIMIZER
         if (div(opd).div1 == 16 && div(opd).ro == div(opd).rn) {
           // divide 32/16
+          tmp = rcache_get_tmp_arg(1);
+          emith_add_r_r_ptr_imm(tmp, CONTEXT_REG, offsetof(SH2, drc_tmp));
           rcache_get_reg_arg(0, div(opd).rn, NULL);
-          tmp2 = rcache_get_reg_arg(1, div(opd).rm, NULL);
+          tmp2 = rcache_get_reg_arg(2, div(opd).rm, NULL);
           tmp3 = rcache_get_tmp();
           emith_lsr(tmp3, tmp2, 31);
           emith_or_r_r_lsl(sr, tmp3, M_SHIFT);        // M = Rm[31]
@@ -4052,19 +4088,22 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
           emith_and_r_r_imm(tmp3, tmp3, 1);
           emith_eor_r_r_imm(tmp3, tmp3, 1);
           emith_or_r_r_lsl(sr, tmp3, Q_SHIFT);        // Q = !Rn[0]^M
+          emith_ctx_read(tmp3, offsetof(SH2, drc_tmp));
+          emith_or_r_r_r(sr, sr, tmp3);               // T
           rcache_free_tmp(tmp3);
-          emith_or_r_r_r_lsr(sr, sr, tmp2, 31);       // T = Rn[31]
           skip_op = div(opd).div1 + div(opd).rotcl;
         }
         else if (div(opd).div1 == 32 && div(opd).ro != div(opd).rn) {
           // divide 64/32
           tmp4 = rcache_get_reg(div(opd).ro, RC_GR_READ, NULL);
           emith_ctx_write(tmp4, offsetof(SH2, drc_tmp));
-          rcache_get_reg_arg(0, div(opd).rn, NULL);
+          tmp  = rcache_get_reg_arg(0, div(opd).rn, NULL);
           tmp2 = rcache_get_reg_arg(2, div(opd).rm, NULL);
           tmp3 = rcache_get_tmp_arg(1);
           emith_lsr(tmp3, tmp2, 31);
-          emith_or_r_r_lsl(sr, tmp3, M_SHIFT);         // M = Rm[31]
+          emith_or_r_r_lsl(sr, tmp3, M_SHIFT);        // M = Rm[31]
+          emith_eor_r_r_lsr(tmp3, tmp, 31);
+          emith_or_r_r(sr, tmp3);                     // T = Rn[31]^M
           emith_add_r_r_ptr_imm(tmp3, CONTEXT_REG, offsetof(SH2, drc_tmp));
           rcache_invalidate_tmp();
           emith_abicall(sh2_drc_divs64);
@@ -4081,7 +4120,6 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
           emith_eor_r_r_imm(tmp3, tmp3, 1);
           emith_or_r_r_lsl(sr, tmp3, Q_SHIFT);        // Q = !Ro[0]^M
           rcache_free_tmp(tmp3);
-          emith_or_r_r_r_lsr(sr, sr, tmp4, 31);       // T = Ro[31]
           skip_op = div(opd).div1 + div(opd).rotcl;
         } else
 #endif
@@ -5638,7 +5676,10 @@ int sh2_execute_drc(SH2 *sh2c, int cycles)
   // bit11 contains T saved for delay slot
   // others are usual SH2 flags
   sh2c->sr &= 0x3f3;
-  sh2c->sr |= cycles << 12;
+  sh2c->sr |= (cycles-1) << 12;
+#if (DRC_DEBUG & 8)
+  lastpc = lastcnt = 0;
+#endif
 
   sh2c->state |= SH2_IN_DRC;
   sh2_drc_entry(sh2c);
@@ -5646,11 +5687,16 @@ int sh2_execute_drc(SH2 *sh2c, int cycles)
 
   // TODO: irq cycles
   ret_cycles = (int32_t)sh2c->sr >> 12;
-  if (ret_cycles > 0)
+  if (ret_cycles >= 0)
     dbg(1, "warning: drc returned with cycles: %d, pc %08x", ret_cycles, sh2c->pc);
+#if (DRC_DEBUG & 8)
+  if (lastcnt)
+    dbg(8, "= %csh2 enter %08x %p (%d times), c=%d", sh2c->is_slave?'s':'m',
+      lastpc, lastblock, lastcnt, (signed int)sh2c->sr >> 12);
+#endif
 
   sh2c->sr &= 0x3f3;
-  return ret_cycles;
+  return ret_cycles+1;
 }
 
 static void block_stats(void)
@@ -6177,6 +6223,7 @@ u16 scan_block(u32 base_pc, int is_slave, u8 *op_flags, u32 *end_pc_out,
           break;
         case 1: // SLEEP      0000000000011011
           opd->op = OP_SLEEP;
+          opd->cycles = 3;
           end_block = 1;
           break;
         case 2: // RTE        0000000000101011

+ 4 - 3
cpu/sh2/compiler.h

@@ -47,7 +47,7 @@ u16 scan_block(u32 base_pc, int is_slave, u8 *op_flags, u32 *end_pc,
 #elif defined(__riscv__) || defined(__riscv)
 #define	DRC_SR_REG	"s11"
 #define DRC_REG_LL	0	// no ABI for (__ILP32__ && __riscv_xlen != 32)
-#elif defined(__powerpc__)
+#elif defined(__powerpc__) || defined(__ppc__)
 #define	DRC_SR_REG	"r28"
 #define DRC_REG_LL	0	// no ABI for __ILP32__
 #elif defined(__i386__)
@@ -70,12 +70,13 @@ extern void REGPARM(1) (*sh2_drc_restore_sr)(SH2 *sh2);
 #else
 #define	DRC_DECLARE_SR	register long		_sh2_sr asm(DRC_SR_REG)
 #endif
+// NB: save/load SR register only when DRC is executing and not in DMA access
 #define DRC_SAVE_SR(sh2) \
-    if (likely(sh2->state & SH2_IN_DRC)) \
+    if (likely((sh2->state & (SH2_IN_DRC|SH2_STATE_SLEEP)) == SH2_IN_DRC)) \
 	sh2->sr = (s32)_sh2_sr
 //      sh2_drc_save_sr(sh2)
 #define DRC_RESTORE_SR(sh2) \
-    if (likely(sh2->state & SH2_IN_DRC)) \
+    if (likely((sh2->state & (SH2_IN_DRC|SH2_STATE_SLEEP)) == SH2_IN_DRC)) \
 	_sh2_sr = (s32)sh2->sr
 //      sh2_drc_restore_sr(sh2)
 #else

+ 7 - 5
cpu/sh2/mame/sh2pico.c

@@ -167,14 +167,16 @@ int sh2_execute_interpreter(SH2 *sh2, int cycles)
 
 		sh2->icount--;
 
-		if (sh2->test_irq && !sh2->delay && sh2->pending_level > ((sh2->sr >> 4) & 0x0f))
+		if (sh2->test_irq && !sh2->delay)
 		{
 			int level = sh2->pending_level;
-			int vector = sh2->irq_callback(sh2, level);
-			sh2_do_irq(sh2, level, vector);
+			if (level > ((sh2->sr >> 4) & 0x0f))
+			{
+				int vector = sh2->irq_callback(sh2, level);
+				sh2_do_irq(sh2, level, vector);
+			}
 			sh2->test_irq = 0;
 		}
-
 	}
 	while (sh2->icount > 0 || sh2->delay);	/* can't interrupt before delay */
 
@@ -221,7 +223,7 @@ int sh2_execute_interpreter(SH2 *sh2, int cycles)
 				|| pc_expect != sh2->pc) // branched
 			{
 				pc_expect = sh2->pc;
-				if (sh2->icount < 0)
+				if (sh2->icount <= 0)
 					break;
 			}
 

+ 2 - 2
cpu/sh2/sh2.h

@@ -50,8 +50,8 @@ typedef struct SH2_
 #define SH2_STATE_CPOLL (1 << 2)	// polling comm regs
 #define SH2_STATE_VPOLL (1 << 3)	// polling VDP
 #define SH2_STATE_RPOLL (1 << 4)	// polling address in SDRAM
-#define SH2_TIMER_RUN   (1 << 7)	// SOC WDT timer is running
-#define SH2_IN_DRC      (1 << 8)	// DRC in use
+#define SH2_TIMER_RUN   (1 << 6)	// SOC WDT timer is running
+#define SH2_IN_DRC      (1 << 7)	// DRC in use
 	unsigned int	state;
 	uint32_t	poll_addr;
 	int		poll_cycles;

+ 10 - 10
pico/32x/32x.c

@@ -56,14 +56,14 @@ void p32x_update_irls(SH2 *active_sh2, unsigned int m68k_cycles)
   if (mrun) {
     p32x_sh2_poll_event(&msh2, SH2_IDLE_STATES, m68k_cycles);
     if (msh2.state & SH2_STATE_RUN)
-      sh2_end_run(&msh2, 1);
+      sh2_end_run(&msh2, 0);
   }
 
   srun = sh2_irl_irq(&ssh2, slvl, ssh2.state & SH2_STATE_RUN);
   if (srun) {
     p32x_sh2_poll_event(&ssh2, SH2_IDLE_STATES, m68k_cycles);
     if (ssh2.state & SH2_STATE_RUN)
-      sh2_end_run(&ssh2, 1);
+      sh2_end_run(&ssh2, 0);
   }
 
   elprintf(EL_32X, "update_irls: m %d/%d, s %d/%d", mlvl, mrun, slvl, srun);
@@ -142,9 +142,9 @@ void p32x_reset_sh2s(void)
       unsigned int vbr;
 
       // initial data
-      idl_src = CPU_BE2(*(unsigned int *)(Pico.rom + 0x3d4)) & ~0xf0000000;
-      idl_dst = CPU_BE2(*(unsigned int *)(Pico.rom + 0x3d8)) & ~0xf0000000;
-      idl_size= CPU_BE2(*(unsigned int *)(Pico.rom + 0x3dc));
+      idl_src = CPU_BE2(*(u32 *)(Pico.rom + 0x3d4)) & ~0xf0000000;
+      idl_dst = CPU_BE2(*(u32 *)(Pico.rom + 0x3d8)) & ~0xf0000000;
+      idl_size= CPU_BE2(*(u32 *)(Pico.rom + 0x3dc));
       if (idl_size > Pico.romsize || idl_src + idl_size > Pico.romsize ||
           idl_size > 0x40000 || idl_dst + idl_size > 0x40000 || (idl_src & 3) || (idl_dst & 3)) {
         elprintf(EL_STATUS|EL_ANOMALY, "32x: invalid initial data ptrs: %06x -> %06x, %06x",
@@ -154,11 +154,11 @@ void p32x_reset_sh2s(void)
         memcpy(Pico32xMem->sdram + idl_dst, Pico.rom + idl_src, idl_size);
 
       // VBR
-      vbr = CPU_BE2(*(unsigned int *)(Pico.rom + 0x3e8));
+      vbr = CPU_BE2(*(u32 *)(Pico.rom + 0x3e8));
       sh2_set_vbr(0, vbr);
 
       // checksum and M_OK
-      Pico32x.regs[0x28 / 2] = *(unsigned short *)(Pico.rom + 0x18e);
+      Pico32x.regs[0x28 / 2] = *(u16 *)(Pico.rom + 0x18e);
     }
     // program will set M_OK
   }
@@ -168,7 +168,7 @@ void p32x_reset_sh2s(void)
     unsigned int vbr;
 
     // GBR/VBR
-    vbr = CPU_BE2(*(unsigned int *)(Pico.rom + 0x3ec));
+    vbr = CPU_BE2(*(u32 *)(Pico.rom + 0x3ec));
     sh2_set_gbr(1, 0x20004000);
     sh2_set_vbr(1, vbr);
     // program will set S_OK
@@ -328,7 +328,7 @@ void p32x_event_schedule_sh2(SH2 *sh2, enum p32x_event event, int after)
   left_to_next = C_M68K_TO_SH2(sh2, (int)(event_time_next - now));
   if (sh2_cycles_left(sh2) > left_to_next) {
     if (left_to_next < 1)
-      left_to_next = 1;
+      left_to_next = 0;
     sh2_end_run(sh2, left_to_next);
   }
 }
@@ -421,7 +421,7 @@ void p32x_sync_other_sh2(SH2 *sh2, unsigned int m68k_target)
     left_to_event = C_M68K_TO_SH2(sh2, (int)(event_time_next - m68k_target));
     if (sh2_cycles_left(sh2) > left_to_event) {
       if (left_to_event < 1)
-        left_to_event = 1;
+        left_to_event = 0;
       sh2_end_run(sh2, left_to_event);
     }
   }

+ 1 - 1
pico/32x/draw.c

@@ -10,7 +10,7 @@
 
 // BGR555 to native conversion
 #if defined(USE_BGR555)
-#define PXCONV(t)   (t)
+#define PXCONV(t)   ((t)&(mr|mg|mb|mp))
 #define PXPRIO      0x8000  // prio in MSB
 #elif defined(USE_BGR565)
 #define PXCONV(t)   (((t)&mr)  | (((t)&(mg|mb)) << 1) | (((t)&mp) >> 10))

+ 52 - 17
pico/32x/draw_arm.S

@@ -101,10 +101,46 @@
     mov     r6, #320
     add     r5, r1, r12, lsl #1 @ p32x = dram + dram[l]
 
+    ldrh    r7, [r5], #2
 2: @ loop_inner:
-    ldrh    r8, [r5], #2
+    mov     r8, r7
     subs    lr, r6, #1
     blt     0b @ loop_outer
+    beq     7f @ single_pix
+    ldrh    r7, [r5], #2     @ 32x pixel
+    cmp     r7, r8           @ do RLE only if we have at least 2 px
+@    ldreqh  r7, [r5]
+@    cmpeq   r7, r8
+    subeq   lr, lr, #1
+    beq     3f @ loop_innermost
+
+7: @ single_pix:
+    mov     r6, lr
+
+    eor     r12,r8, r10
+    tst     r12, #0x8000     @ !((t ^ inv) & 0x8000)
+    addeq   r11,r11,#1
+    beq     8f @ single_pix_32x
+
+    ldrb    r12,[r11], #1    @ MD pixel
+    cmp     r3, r12,lsl #26  @ MD has bg pixel?
+.if \do_md
+    movne   r12,r12,lsl #1
+    ldrneh  r12,[r9, r12]
+    strneh  r12,[r0], #2     @ *dst++ = palmd[*pmd]
+.else
+    addne   r0, r0, #2
+.endif
+    bne     2b @ loop_inner
+
+8: @ single_pix_32x:
+    and     r12,r8, #0x03e0
+    mov     r8, r8, lsl #11
+    orr     r8, r8, r8, lsr #(10+11)
+    orr     r8, r8, r12,lsl #1
+    bic     r8, r8, #0x0020  @ kill prio bit
+    strh    r8, [r0], #2
+    b       2b @ loop_inner
 
 3: @ loop_innermost:
     ldrh    r7, [r5], #2     @ 32x pixel
@@ -112,7 +148,6 @@
     cmpge   r7, r8
     beq     3b @ loop_innermost
 
-    sub     r5, r5, #2
     add     lr, lr, #1
     sub     lr, r6, lr
     sub     r6, r6, lr
@@ -121,10 +156,10 @@
     tst     r12, #0x8000     @ !((t ^ inv) & 0x8000)
     bne     5f @ draw_md
 
-    and     r7 ,r8, #0x03e0
+    and     r12,r8, #0x03e0
     mov     r8, r8, lsl #11
     orr     r8, r8, r8, lsr #(10+11)
-    orr     r8, r8, r7 ,lsl #1
+    orr     r8, r8, r12,lsl #1
     bic     r8, r8, #0x0020  @ kill prio bit
 
     add     r11,r11,lr
@@ -148,35 +183,35 @@
 
 5: @ draw_md:
     subs    lr, lr, #1
-    ldrgeb  r7, [r11], #1    @ MD pixel
+    ldrgeb  r12,[r11], #1    @ MD pixel
     blt     2b @ loop_inner
-    cmp     r3, r7, lsl #26  @ MD has bg pixel?
+    cmp     r3, r12,lsl #26  @ MD has bg pixel?
 .if \do_md
-    mov     r7, r7, lsl #1
-    ldrneh  r7 ,[r9, r7]
-    strneh  r7 ,[r0], #2     @ *dst++ = palmd[*pmd]
+    mov     r12,r12,lsl #1
+    ldrneh  r12,[r9, r12]
+    strneh  r12,[r0], #2     @ *dst++ = palmd[*pmd]
 .else
     addne   r0, r0, #2
 .endif
     bne     5b @ draw_md
 
-    and     r7 ,r8, #0x03e0
+    and     r12,r8, #0x03e0
     mov     r8, r8, lsl #11
     orr     r8, r8, r8, lsr #(10+11)
-    orr     r8, r8, r7 ,lsl #1
+    orr     r8, r8, r12,lsl #1
     bic     r8, r8, #0x0020  @ kill prio bit
     strh    r8, [r0], #2     @ *dst++ = bgr2rgb(*p32x++)
 
 6: @ draw_md_32x:
     subs    lr, lr, #1
-    ldrgeb  r7, [r11], #1    @ MD pixel
+    ldrgeb  r12,[r11], #1    @ MD pixel
     blt     2b @ loop_inner
-    cmp     r3, r7, lsl #26  @ MD has bg pixel?
+    cmp     r3, r12,lsl #26  @ MD has bg pixel?
 .if \do_md
-    mov     r7, r7, lsl #1
-    ldrneh  r7 ,[r9, r7]     @ *dst++ = palmd[*pmd]
-    moveq   r7 ,r8           @ *dst++ = bgr2rgb(*p32x++)
-    strh    r7 ,[r0], #2
+    mov     r12,r12,lsl #1
+    ldrneh  r12,[r9, r12]    @ *dst++ = palmd[*pmd]
+    moveq   r12,r8           @ *dst++ = bgr2rgb(*p32x++)
+    strh    r12,[r0], #2
 .else
     streqh  r8, [r0]         @ *dst++ = bgr2rgb(*p32x++)
     add     r0, r0, #2

+ 27 - 22
pico/32x/memory.c

@@ -119,14 +119,14 @@ void NOINLINE p32x_sh2_poll_detect(u32 a, SH2 *sh2, u32 flags, int maxcnt)
   // reading 2 consecutive 16bit values is probably a 32bit access. detect this
   // by checking address (max 2 bytes away) and cycles (max 2 cycles later).
   // no polling if more than 20 cycles have passed since last detect call.
-  if (a - sh2->poll_addr <= 2 && CYCLES_GE(20, cycles_diff)) {
+  if (a - sh2->poll_addr <= 2 && CYCLES_GE(10, cycles_diff)) {
     if (CYCLES_GT(cycles_diff, 2) && ++sh2->poll_cnt >= maxcnt) {
       if (!(sh2->state & flags))
         elprintf_sh2(sh2, EL_32X, "state: %02x->%02x",
           sh2->state, sh2->state | flags);
 
       sh2->state |= flags;
-      sh2_end_run(sh2, 1);
+      sh2_end_run(sh2, 0);
       pevt_log_sh2(sh2, EVT_POLL_START);
 #ifdef DRC_SH2
       // mark this as an address used for polling if SDRAM
@@ -324,7 +324,7 @@ static u32 sh2_comm_faker(u32 a)
   static int f = 0;
   if (a == 0x28 && !p32x_csum_faked) {
     p32x_csum_faked = 1;
-    return *(unsigned short *)(Pico.rom + 0x18e);
+    return *(u16 *)(Pico.rom + 0x18e);
   }
   if (f >= sizeof(comm_fakevals) / sizeof(comm_fakevals[0]))
     f = 0;
@@ -511,7 +511,7 @@ static void p32x_reg_write8(u32 a, u32 d)
     case 0x2d:
     case 0x2e:
     case 0x2f:
-      if (REG8IN16(r, a) != d) {
+      if (REG8IN16(r, a) != (u8)d) {
         unsigned int cycles = SekCyclesDone();
 
         if (CYCLES_GT(cycles - msh2.m68krcycles_done, 64))
@@ -608,7 +608,7 @@ static void p32x_reg_write16(u32 a, u32 d)
     case 0x2a/2:
     case 0x2c/2:
     case 0x2e/2:
-      if (r[a / 2] != d) {
+      if (r[a / 2] != (u16)d) {
         unsigned int cycles = SekCyclesDone();
 
         if (CYCLES_GT(cycles - msh2.m68krcycles_done, 64))
@@ -736,7 +736,7 @@ static u32 p32x_sh2reg_read16(u32 a, SH2 *sh2)
       return (r[0] & P32XS_FM) | Pico32x.sh2_regs[0]
         | Pico32x.sh2irq_mask[sh2->is_slave];
     case 0x04/2: // H count (often as comm too)
-      p32x_sh2_poll_detect(a, sh2, SH2_STATE_CPOLL, 9);
+      p32x_sh2_poll_detect(a, sh2, SH2_STATE_CPOLL, 5);
       cycles = sh2_cycles_done_m68k(sh2);
       sh2s_sync_on_read(sh2, cycles);
       return sh2_poll_read(a, Pico32x.sh2_regs[4 / 2], cycles, sh2);
@@ -770,7 +770,7 @@ static u32 p32x_sh2reg_read16(u32 a, SH2 *sh2)
     case 0x2a/2:
     case 0x2c/2:
     case 0x2e/2:
-      p32x_sh2_poll_detect(a, sh2, SH2_STATE_CPOLL, 9);
+      p32x_sh2_poll_detect(a, sh2, SH2_STATE_CPOLL, 5);
       cycles = sh2_cycles_done_m68k(sh2);
       sh2s_sync_on_read(sh2, cycles);
       return sh2_poll_read(a, r[a / 2], cycles, sh2);
@@ -823,7 +823,7 @@ static void p32x_sh2reg_write8(u32 a, u32 d, SH2 *sh2)
       return;
     case 0x05: // H count
       d &= 0xff;
-      if (Pico32x.sh2_regs[4 / 2] != d) {
+      if (Pico32x.sh2_regs[4 / 2] != (u8)d) {
         unsigned int cycles = sh2_cycles_done_m68k(sh2);
         Pico32x.sh2_regs[4 / 2] = d;
         p32x_sh2_poll_event(sh2->other_sh2, SH2_STATE_CPOLL, cycles);
@@ -848,14 +848,14 @@ static void p32x_sh2reg_write8(u32 a, u32 d, SH2 *sh2)
     case 0x2d:
     case 0x2e:
     case 0x2f:
-      if (REG8IN16(r, a) != d) {
+      if (REG8IN16(r, a) != (u8)d) {
         unsigned int cycles = sh2_cycles_done_m68k(sh2);
 
         REG8IN16(r, a) = d;
         p32x_m68k_poll_event(P32XF_68KCPOLL);
         p32x_sh2_poll_event(sh2->other_sh2, SH2_STATE_CPOLL, cycles);
         if (p32x_sh2_ready(sh2->other_sh2, cycles+8))
-          sh2_end_run(sh2, 1);
+          sh2_end_run(sh2, 0);
         sh2_poll_write(a & ~1, r[a / 2], cycles, sh2);
       }
       return;
@@ -941,14 +941,14 @@ static void p32x_sh2reg_write16(u32 a, u32 d, SH2 *sh2)
     case 0x2a/2:
     case 0x2c/2:
     case 0x2e/2:
-      if (Pico32x.regs[a / 2] != d) {
+      if (Pico32x.regs[a / 2] != (u16)d) {
         unsigned int cycles = sh2_cycles_done_m68k(sh2);
 
         Pico32x.regs[a / 2] = d;
         p32x_m68k_poll_event(P32XF_68KCPOLL);
         p32x_sh2_poll_event(sh2->other_sh2, SH2_STATE_CPOLL, cycles);
         if (p32x_sh2_ready(sh2->other_sh2, cycles+8))
-          sh2_end_run(sh2, 1);
+          sh2_end_run(sh2, 0);
         sh2_poll_write(a, d, cycles, sh2);
       }
       return;
@@ -1457,7 +1457,7 @@ static u32 REGPARM(2) sh2_read8_cs0(u32 a, SH2 *sh2)
 
   if ((a & 0x3fff0) == 0x4100) {
     d = p32x_vdp_read16(a);
-    p32x_sh2_poll_detect(a, sh2, SH2_STATE_VPOLL, 9);
+    p32x_sh2_poll_detect(a, sh2, SH2_STATE_VPOLL, 7);
     goto out_16to8;
   }
 
@@ -1520,7 +1520,7 @@ static u32 REGPARM(2) sh2_read16_cs0(u32 a, SH2 *sh2)
 
   if ((a & 0x3fff0) == 0x4100) {
     d = p32x_vdp_read16(a);
-    p32x_sh2_poll_detect(a, sh2, SH2_STATE_VPOLL, 9);
+    p32x_sh2_poll_detect(a, sh2, SH2_STATE_VPOLL, 7);
     goto out;
   }
 
@@ -1583,7 +1583,7 @@ static void sh2_sdram_poll(u32 a, u32 d, SH2 *sh2)
   sh2_poll_write(a, d, cycles, sh2);
   p32x_sh2_poll_event(sh2->other_sh2, SH2_STATE_RPOLL, cycles);
   if (p32x_sh2_ready(sh2->other_sh2, cycles+8))
-    sh2_end_run(sh2, 1);
+    sh2_end_run(sh2, 0);
   DRC_RESTORE_SR(sh2);
 }
 
@@ -1690,16 +1690,21 @@ static void REGPARM(3) sh2_write8_da(u32 a, u32 d, SH2 *sh2)
 }
 #endif
 
+static NOINLINE void REGPARM(3) sh2_write8_sdram_sync(u32 a, u32 d, SH2 *sh2)
+{
+  DRC_SAVE_SR(sh2);
+  sh2_end_run(sh2, 32);
+  DRC_RESTORE_SR(sh2);
+  sh2_write8_sdram(a, d, sh2);
+}
+
 static void REGPARM(3) sh2_write8_sdram_wt(u32 a, u32 d, SH2 *sh2)
 {
   // xmen sync hack..
-  if (a < 0x26000200) {
-    DRC_SAVE_SR(sh2);
-    sh2_end_run(sh2, 32);
-    DRC_RESTORE_SR(sh2);
-  }
-
-  sh2_write8_sdram(a, d, sh2);
+  if ((a << 8) >> 17) // ((a & 0x00ffffff) < 0x200)
+    sh2_write8_sdram(a, d, sh2);
+  else
+    sh2_write8_sdram_sync(a, d, sh2);
 }
 
 // write16

+ 4 - 0
pico/32x/sh2soc.c

@@ -480,10 +480,12 @@ void REGPARM(3) sh2_peripheral_write32(u32 a, u32 d, SH2 *sh2)
       if (!(dmac->dmaor & DMA_DME))
         return;
 
+      DRC_SAVE_SR(sh2);
       if ((dmac->chan[0].chcr & (DMA_TE|DMA_DE)) == DMA_DE)
         dmac_trigger(sh2, &dmac->chan[0]);
       if ((dmac->chan[1].chcr & (DMA_TE|DMA_DE)) == DMA_DE)
         dmac_trigger(sh2, &dmac->chan[1]);
+      DRC_RESTORE_SR(sh2);
       break;
   }
 
@@ -538,7 +540,9 @@ static void dreq1_do(SH2 *sh2, struct dma_chan *chan)
   if ((chan->dar & ~0xf) != 0x20004030)
     elprintf(EL_32XP|EL_ANOMALY, "dreq1: bad dar?: %08x\n", chan->dar);
 
+  sh2->state |= SH2_STATE_SLEEP;
   dmac_transfer_one(sh2, chan);
+  sh2->state &= ~SH2_STATE_SLEEP;
   if (chan->tcr == 0)
     dmac_transfer_complete(sh2, chan);
 }

+ 9 - 7
pico/cart.c

@@ -187,7 +187,7 @@ zip_failed:
   else if (strcasecmp(ext, "cso") == 0)
   {
     cso_struct *cso = NULL, *tmp = NULL;
-    int size;
+    int i, size;
     f = fopen(path, "rb");
     if (f == NULL)
       goto cso_failed;
@@ -229,6 +229,8 @@ zip_failed:
       elprintf(EL_STATUS, "cso: premature EOF");
       goto cso_failed;
     }
+    for (i = 0; i < size/4; i++)
+      cso->index[i] = CPU_LE4(cso->index[i]);
 
     // all ok
     cso->fpos_in = ftell(f);
@@ -280,7 +282,7 @@ cso_failed:
     chd->file.file = chd;
     chd->file.type = PMT_CHD;
     // subchannel data is skipped, remove it from total size
-    chd->file.size = chd_get_header(cf)->logicalbytes / CD_FRAME_SIZE * CD_MAX_SECTOR_DATA;
+    chd->file.size = head->logicalbytes / CD_FRAME_SIZE * CD_MAX_SECTOR_DATA;
     strncpy(chd->file.ext, ext, sizeof(chd->file.ext) - 1);
     return &chd->file;
 
@@ -359,7 +361,7 @@ static size_t _pm_read_chd(void *ptr, size_t bytes, pm_file *stream, int is_audi
       if (len > bytes)
         len = bytes;
 
-#ifdef CPU_IS_LE
+#if CPU_IS_LE
       if (is_audio) {
         // convert big endian audio samples
         u16 *dst = ptr, v;
@@ -821,7 +823,7 @@ int PicoCartInsert(unsigned char *rom, unsigned int romsize, const char *carthw_
   // This will hang the emu, but will prevent nasty crashes.
   // note: 4 bytes are padded to every ROM
   if (rom != NULL)
-    *(unsigned long *)(rom+romsize) = CPU_BE2(0x4EFAFFFE);
+    *(u32 *)(rom+romsize) = CPU_BE2(0x4EFAFFFE);
 
   Pico.rom=rom;
   Pico.romsize=romsize;
@@ -1254,11 +1256,11 @@ static void PicoCartDetect(const char *carthw_cfg)
   int fill_sram = 0;
 
   memset(&Pico.sv, 0, sizeof(Pico.sv));
-  if (Pico.rom[0x1B1] == 'R' && Pico.rom[0x1B0] == 'A')
+  if (Pico.rom[MEM_BE2(0x1B0)] == 'R' && Pico.rom[MEM_BE2(0x1B1)] == 'A')
   {
     Pico.sv.start =  rom_read32(0x1B4) & ~0xff000001; // align
     Pico.sv.end   = (rom_read32(0x1B8) & ~0xff000000) | 1;
-    if (Pico.rom[0x1B2] & 0x40)
+    if (Pico.rom[MEM_BE2(0x1B3)] & 0x40)
       // EEPROM
       Pico.sv.flags |= SRF_EEPROM;
     Pico.sv.flags |= SRF_ENABLED;
@@ -1309,7 +1311,7 @@ static void PicoCartDetect(const char *carthw_cfg)
 
   // Unusual region 'code'
   if (rom_strcmp(0x1f0, "EUROPE") == 0 || rom_strcmp(0x1f0, "Europe") == 0)
-    *(int *) (Pico.rom + 0x1f0) = 0x20204520;
+    *(u32 *) (Pico.rom + 0x1f0) = CPU_LE4(0x20204520);
 }
 
 // vim:shiftwidth=2:expandtab

+ 7 - 7
pico/cd/cdc.c

@@ -367,7 +367,7 @@ void cdc_dma_update(void)
       {
         /* update IRQ level */
         elprintf(EL_INTS, "cdc DTE irq 5");
-        SekInterruptS68k(5);
+        pcd_irq_s68k(5, 1);
       }
     }
 
@@ -411,7 +411,7 @@ int cdc_decoder_update(uint8 header[4])
       {
         /* update IRQ level */
         elprintf(EL_INTS, "cdc DEC irq 5");
-        SekInterruptS68k(5);
+        pcd_irq_s68k(5, 1);
       }
     }
 
@@ -469,13 +469,13 @@ void cdc_reg_w(unsigned char data)
         {
           /* update IRQ level */
           elprintf(EL_INTS, "cdc pending irq 5");
-          SekInterruptS68k(5);
+          pcd_irq_s68k(5, 1);
         }
       }
       else // if (scd.pending & (1 << 5))
       {
         /* clear pending level 5 interrupts */
-        SekInterruptClearS68k(5);
+        pcd_irq_s68k(5, 0);
       }
 
       /* abort any data transfer if data output is disabled */
@@ -614,7 +614,7 @@ void cdc_reg_w(unsigned char data)
       if ((cdc.ifstat | BIT_DECI) || !(cdc.ifctrl & BIT_DECIEN))
       {
         /* clear pending level 5 interrupt */
-        SekInterruptClearS68k(5);
+        pcd_irq_s68k(5, 0);
       }
 #endif
       Pico_mcd->s68k_regs[0x04+1] = 0x08;
@@ -774,7 +774,7 @@ unsigned char cdc_reg_r(void)
       if ((cdc.ifstat | BIT_DTEI) || !(cdc.ifctrl & BIT_DTEIEN))
       {
         /* clear pending level 5 interrupt */
-        SekInterruptClearS68k(5);
+        pcd_irq_s68k(5, 0);
       }
 #endif
 
@@ -826,7 +826,7 @@ unsigned short cdc_host_r(void)
         {
           /* update IRQ level */
           elprintf(EL_INTS, "cdc DTE irq 5");
-          SekInterruptS68k(5);
+          pcd_irq_s68k(5, 1);
         }
       }
 

+ 3 - 0
pico/cd/cdd.c

@@ -444,6 +444,9 @@ int cdd_unload(void)
   {
     int i;
 
+    /* stop audio streaming */
+    Pico_mcd->cdda_stream = NULL;
+
     /* close CD tracks */
     if (cdd.toc.tracks[0].fd)
     {

+ 1 - 1
pico/cd/gfx.c

@@ -421,7 +421,7 @@ void gfx_update(unsigned int cycles)
 
     if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN1) {
       elprintf(EL_INTS|EL_CD, "s68k: gfx_cd irq 1");
-      SekInterruptS68k(1);
+      pcd_irq_s68k(1, 1);
     }
   }
   else {

+ 1 - 1
pico/cd/libchdr

@@ -1 +1 @@
-Subproject commit ff3175ddb040b1a967c665a28868b16bf5aa97dd
+Subproject commit 00319cf31f034e4d468a49a60265c7c5b8305b70

+ 41 - 19
pico/cd/mcd.c

@@ -124,9 +124,6 @@ static void SekRunS68k(unsigned int to)
   if ((cyc_do = SekCycleAimS68k - SekCycleCntS68k) <= 0)
     return;
 
-  if (SekShouldInterrupt())
-    Pico_mcd->m.s68k_poll_a = 0;
-
   pprof_start(s68k);
   SekCycleCntS68k += cyc_do;
 #if defined(EMU_C68K)
@@ -177,7 +174,7 @@ static void pcd_cdc_event(unsigned int now)
 
     if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN4) {
       elprintf(EL_INTS|EL_CD, "s68k: cdd irq 4");
-      SekInterruptS68k(4);
+      pcd_irq_s68k(4, 1);
     }
   }
 
@@ -188,7 +185,7 @@ static void pcd_int3_timer_event(unsigned int now)
 {
   if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN3) {
     elprintf(EL_INTS|EL_CD, "s68k: timer irq 3");
-    SekInterruptS68k(3);
+    pcd_irq_s68k(3, 1);
   }
 
   if (Pico_mcd->s68k_regs[0x31] != 0)
@@ -280,6 +277,17 @@ static void pcd_run_events(unsigned int until)
       oldest, event_time_next);
 }
 
+void pcd_irq_s68k(int irq, int state)
+{
+  if (state) {
+    SekInterruptS68k(irq);
+    if (SekIsStoppedS68k())
+      SekSetStopS68k(0);
+    Pico_mcd->m.s68k_poll_a = 0;
+  } else
+    SekInterruptClearS68k(irq);
+}
+
 int pcd_sync_s68k(unsigned int m68k_target, int m68k_poll_sync)
 {
   #define now SekCycleCntS68k
@@ -307,7 +315,11 @@ int pcd_sync_s68k(unsigned int m68k_target, int m68k_poll_sync)
     if (event_time_next && CYCLES_GT(target, event_time_next))
       target = event_time_next;
 
-    SekRunS68k(target);
+    if (SekIsStoppedS68k())
+      SekCycleCntS68k = SekCycleAimS68k = target;
+    else
+      SekRunS68k(target);
+
     if (m68k_poll_sync && Pico_mcd->m.m68k_poll_cnt == 0)
       break;
   }
@@ -324,21 +336,31 @@ static void SekSyncM68k(void);
 void pcd_run_cpus_normal(int m68k_cycles)
 {
   Pico.t.m68c_aim += m68k_cycles;
-  if (SekShouldInterrupt() || Pico_mcd->m.m68k_poll_cnt < 12)
-    Pico_mcd->m.m68k_poll_cnt = 0;
-  else if (Pico_mcd->m.m68k_poll_cnt >= 16) {
-    int s68k_left = pcd_sync_s68k(Pico.t.m68c_aim, 1);
-    if (s68k_left <= 0) {
-      elprintf(EL_CDPOLL, "m68k poll [%02x] x%d @%06x",
-        Pico_mcd->m.m68k_poll_a, Pico_mcd->m.m68k_poll_cnt, SekPc);
-      Pico.t.m68c_cnt = Pico.t.m68c_aim;
-      return;
-    }
-    Pico.t.m68c_cnt = Pico.t.m68c_aim - (s68k_left * mcd_s68k_cycle_mult >> 16);
-  }
 
   while (CYCLES_GT(Pico.t.m68c_aim, Pico.t.m68c_cnt)) {
-    SekRunM68kOnce();
+    if (SekShouldInterrupt())
+      Pico_mcd->m.m68k_poll_cnt = 0;
+
+#ifdef USE_POLL_DETECT
+    if (Pico_mcd->m.m68k_poll_cnt >= 16) {
+      int s68k_left;
+      // main CPU is polling, (wake and) run sub only
+      if (SekIsStoppedS68k())
+        SekSetStopS68k(0);
+      s68k_left = pcd_sync_s68k(Pico.t.m68c_aim, 1);
+
+      Pico.t.m68c_cnt = Pico.t.m68c_aim;
+      if (s68k_left > 0)
+        Pico.t.m68c_cnt -= ((long long)s68k_left * mcd_s68k_cycle_mult >> 16);
+      if (SekIsStoppedS68k()) {
+        // slave has stopped, wake master to avoid lockups
+        Pico_mcd->m.m68k_poll_cnt = 0;
+      }
+      elprintf(EL_CDPOLL, "m68k poll [%02x] x%d @%06x",
+        Pico_mcd->m.m68k_poll_a, Pico_mcd->m.m68k_poll_cnt, SekPc);
+    } else
+#endif
+      SekRunM68kOnce();
     if (Pico_mcd->m.need_sync) {
       Pico_mcd->m.need_sync = 0;
       pcd_sync_s68k(Pico.t.m68c_cnt, 0);

+ 21 - 13
pico/cd/memory.c

@@ -68,20 +68,25 @@ static void remap_word_ram(u32 r3);
 
 void m68k_comm_check(u32 a)
 {
-  pcd_sync_s68k(SekCyclesDone(), 0);
+  u32 cycles = SekCyclesDone();
+  u32 clkdiff = cycles - Pico_mcd->m.m68k_poll_clk;
+  pcd_sync_s68k(cycles, 0);
   if (a >= 0x0e && !Pico_mcd->m.need_sync) {
     // there are cases when slave updates comm and only switches RAM
     // over after that (mcd1b), so there must be a resync..
     SekEndRun(64);
     Pico_mcd->m.need_sync = 1;
   }
-  if (SekNotPolling || a != Pico_mcd->m.m68k_poll_a) {
+  Pico_mcd->m.m68k_poll_clk = cycles;
+  if (SekNotPolling || a != Pico_mcd->m.m68k_poll_a || clkdiff > POLL_CYCLES || clkdiff <= 16) {
     Pico_mcd->m.m68k_poll_a = a;
     Pico_mcd->m.m68k_poll_cnt = 0;
     SekNotPolling = 0;
     return;
   }
   Pico_mcd->m.m68k_poll_cnt++;
+  if(Pico_mcd->m.m68k_poll_cnt == POLL_LIMIT)
+    SekEndRun(0);
 }
 
 #ifndef _ASM_CD_MEMORY_C
@@ -144,13 +149,15 @@ void m68k_reg_write8(u32 a, u32 d)
   u32 dold;
   a &= 0x3f;
 
+  Pico_mcd->m.m68k_poll_cnt = 0;
+
   switch (a) {
     case 0:
       d &= 1;
       if (d && (Pico_mcd->s68k_regs[0x33] & PCDS_IEN2)) {
         elprintf(EL_INTS, "m68k: s68k irq 2");
         pcd_sync_s68k(SekCyclesDone(), 0);
-        SekInterruptS68k(2);
+        pcd_irq_s68k(2, 1);
       }
       return;
     case 1:
@@ -180,8 +187,7 @@ void m68k_reg_write8(u32 a, u32 d)
       return;
     case 2:
       elprintf(EL_CDREGS, "m68k: prg wp=%02x", d);
-      Pico_mcd->s68k_regs[2] = d; // really use s68k side register
-      return;
+      goto write_comm;
     case 3:
       dold = Pico_mcd->s68k_regs[3];
       elprintf(EL_CDREG3, "m68k_regs w3: %02x @%06x", (u8)d, SekPc);
@@ -205,10 +211,10 @@ void m68k_reg_write8(u32 a, u32 d)
 
       goto write_comm;
     case 6:
-      Pico_mcd->bios[0x72 + 1] = d; // simple hint vector changer
+      Pico_mcd->bios[MEM_BE2(0x72)] = d; // simple hint vector changer
       return;
     case 7:
-      Pico_mcd->bios[0x72] = d;
+      Pico_mcd->bios[MEM_BE2(0x73)] = d;
       elprintf(EL_CDREGS, "hint vector set to %04x%04x",
         ((u16 *)Pico_mcd->bios)[0x70/2], ((u16 *)Pico_mcd->bios)[0x72/2]);
       return;
@@ -277,7 +283,7 @@ u32 s68k_poll_detect(u32 a, u32 d)
 
 #define READ_FONT_DATA(basemask) \
 { \
-      unsigned int fnt = *(unsigned int *)(Pico_mcd->s68k_regs + 0x4c); \
+      unsigned int fnt = CPU_LE4(*(u32 *)(Pico_mcd->s68k_regs + 0x4c)); \
       unsigned int col0 = (fnt >> 8) & 0x0f, col1 = (fnt >> 12) & 0x0f;   \
       if (fnt & (basemask << 0)) d  = col1      ; else d  = col0;       \
       if (fnt & (basemask << 1)) d |= col1 <<  4; else d |= col0 <<  4; \
@@ -425,7 +431,7 @@ void s68k_reg_write8(u32 a, u32 d)
         // XXX: emulate pending irq instead?
         if (Pico_mcd->s68k_regs[0x37] & 4) {
           elprintf(EL_INTS, "cdd export irq 4 (unmask)");
-          SekInterruptS68k(4);
+          pcd_irq_s68k(4, 1);
         }
       }
       break;
@@ -443,7 +449,7 @@ void s68k_reg_write8(u32 a, u32 d)
 
         if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN4) {
           elprintf(EL_INTS, "cdd export irq 4");
-          SekInterruptS68k(4);
+          pcd_irq_s68k(4, 1);
         }
       }
       return;
@@ -494,6 +500,8 @@ void s68k_reg_write16(u32 a, u32 d)
 {
   u8 *r = Pico_mcd->s68k_regs;
 
+  Pico_mcd->m.s68k_poll_cnt = 0;
+
   if ((a & 0x1f0) == 0x20)
     goto write_comm;
 
@@ -501,8 +509,8 @@ void s68k_reg_write16(u32 a, u32 d)
     case 0x0e:
       // special case, 2 byte writes would be handled differently
       // TODO: verify
-      r[0xf] = d;
-      return;
+      d = (u8)d | (r[0xe] << 8);
+      goto write_comm;
     case 0x58: // stamp data size
       r[0x59] = d & 7;
       return;
@@ -1094,7 +1102,7 @@ void pcd_state_loaded_mem(void)
   Pico_mcd->m.dmna_ret_2m &= 3;
 
   // restore hint vector
-  *(unsigned short *)(Pico_mcd->bios + 0x72) = Pico_mcd->m.hint_vector;
+  *(u16 *)(Pico_mcd->bios + 0x72) = Pico_mcd->m.hint_vector;
 }
 
 #ifdef EMU_M68K

+ 8 - 8
pico/cd/pcm.c

@@ -88,18 +88,18 @@ void pcd_pcm_sync(unsigned int to)
     }
 
     addr = ch->addr;
-    inc = *(unsigned short *)&ch->regs[2];
-    mul_l = ((int)ch->regs[0] * (ch->regs[1] & 0xf)) >> (5+1); 
-    mul_r = ((int)ch->regs[0] * (ch->regs[1] >>  4)) >> (5+1);
+    inc = ch->regs[2] + (ch->regs[3]<<8);
+    mul_l = (int)ch->regs[0] * (ch->regs[1] & 0xf); 
+    mul_r = (int)ch->regs[0] * (ch->regs[1] >>  4);
 
-    for (s = 0; s < steps; s++, addr = (addr + inc) & 0x7FFFFFF)
+    for (s = 0; s < steps; s++, addr = (addr + inc) & 0x07FFFFFF)
     {
       smp = Pico_mcd->pcm_ram[addr >> PCM_STEP_SHIFT];
 
       // test for loop signal
       if (smp == 0xff)
       {
-        addr = *(unsigned short *)&ch->regs[4]; // loop_addr
+        addr = ch->regs[4] + (ch->regs[5]<<8); // loop_addr
         smp = Pico_mcd->pcm_ram[addr];
         addr <<= PCM_STEP_SHIFT;
         if (smp == 0xff)
@@ -109,8 +109,8 @@ void pcd_pcm_sync(unsigned int to)
       if (smp & 0x80)
         smp = -(smp & 0x7f);
 
-      out[s*2  ] += smp * mul_l; // max 128 * 119 = 15232
-      out[s*2+1] += smp * mul_r;
+      out[s*2  ] += (smp * mul_l) >> 5; // max 127 * 255 * 15 / 32 = 15180
+      out[s*2+1] += (smp * mul_r) >> 5;
     }
     ch->addr = addr;
   }
@@ -120,7 +120,7 @@ end:
   Pico_mcd->pcm_mixpos += steps;
 }
 
-void pcd_pcm_update(int *buf32, int length, int stereo)
+void pcd_pcm_update(s32 *buf32, int length, int stereo)
 {
   int step, *pcm;
   int p = 0;

+ 2 - 2
pico/m68kif_cyclone.s

@@ -81,9 +81,9 @@ cyclone_fetch32:
     ldr     r1, [r1, r2, lsl #2]
     bic     r0, r0, #1
     movs    r1, r1, lsl #1
-    ldrcch  r0, [r1, r0]!
+    ldrcch  r2, [r1, r0]!
     ldrcch  r1, [r1, #2]
-    orrcc   r0, r1, r0, lsl #16
+    orrcc   r0, r1, r2, lsl #16
     bxcc    lr
 
     stmfd   sp!,{r0,r1,r2,lr}

+ 0 - 176
pico/misc.c

@@ -1,7 +1,6 @@
 /*
  * rarely used EEPROM code
  * (C) notaz, 2006-2008
- * (C) kub, 2020
  *
  * This work is licensed under the terms of MAME license.
  * See COPYING file in the top-level directory.
@@ -9,181 +8,6 @@
 
 #include "pico_int.h"
 
-// H-counter table for hvcounter reads in 40col mode, starting at HINT
-const unsigned char hcounts_40[] = {
-0xa5,0xa6,0xa7,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xae,0xaf,0xb0,0xb1,0xb2,
-0xb3,0xb4,0xb5,0xb5,0xe4,0xe5,0xe6,0xe7,0xe8,0xe8,0xe9,0xea,0xea,0xeb,0xec,0xed,
-0xed,0xee,0xef,0xef,0xf0,0xf1,0xf2,0xf2,0xf3,0xf4,0xf4,0xf5,0xf6,0xf7,0xf7,0xf8,
-0xf9,0xfa,0xfb,0xfc,0xfd,0xfd,0xfe,0xff,0x00,0x01,0x02,0x03,0x04,0x04,0x05,0x06,
-0x07,0x08,0x09,0x0a,0x0b,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x12,0x13,0x14,
-0x15,0x16,0x17,0x18,0x19,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x20,0x21,0x22,
-0x23,0x24,0x25,0x26,0x27,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2e,0x2f,0x30,
-0x31,0x32,0x33,0x34,0x35,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3c,0x3d,0x3e,
-0x3f,0x40,0x41,0x42,0x43,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4a,0x4b,0x4c,
-0x4d,0x4e,0x4f,0x50,0x51,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x58,0x59,0x5a,
-0x5b,0x5c,0x5d,0x5e,0x5f,0x5f,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x66,0x67,0x68,
-0x69,0x6a,0x6b,0x6c,0x6d,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x74,0x75,0x76,
-0x77,0x78,0x79,0x7a,0x7b,0x7b,0x7c,0x7d,0x7e,0x7f,0x80,0x81,0x82,0x82,0x83,0x84,
-0x85,0x86,0x87,0x88,0x89,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,0x90,0x90,0x91,0x92,
-0x93,0x94,0x95,0x96,0x97,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9e,0x9f,0xa0,
-0xa1,0xa2,0xa3,0xa4,0xa5,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xac,0xad,0xae,
-};
-
-// H-counter table for hvcounter reads in 32col mode, starting at HINT
-const unsigned char hcounts_32[] = {
-0x85,0x86,0x86,0x87,0x88,0x88,0x89,0x8a,0x8a,0x8b,0x8c,0x8d,0x8d,0x8e,0x8f,0x8f,
-0x90,0x91,0x91,0x92,0x93,0xe9,0xe9,0xea,0xeb,0xeb,0xec,0xed,0xed,0xee,0xef,0xf0,
-0xf0,0xf1,0xf2,0xf2,0xf3,0xf4,0xf4,0xf5,0xf6,0xf7,0xf7,0xf8,0xf9,0xf9,0xfa,0xfb,
-0xfb,0xfc,0xfd,0xfe,0xfe,0xff,0x00,0x00,0x01,0x02,0x02,0x03,0x04,0x05,0x05,0x06,
-0x07,0x07,0x08,0x09,0x09,0x0a,0x0b,0x0c,0x0c,0x0d,0x0e,0x0e,0x0f,0x10,0x10,0x11,
-0x12,0x13,0x13,0x14,0x15,0x15,0x16,0x17,0x17,0x18,0x19,0x1a,0x1a,0x1b,0x1c,0x1c,
-0x1d,0x1e,0x1e,0x1f,0x20,0x21,0x21,0x22,0x23,0x23,0x24,0x25,0x25,0x26,0x27,0x28,
-0x28,0x29,0x2a,0x2a,0x2b,0x2c,0x2c,0x2d,0x2e,0x2f,0x2f,0x30,0x31,0x31,0x32,0x33,
-0x33,0x34,0x35,0x36,0x36,0x37,0x38,0x38,0x39,0x3a,0x3a,0x3b,0x3c,0x3d,0x3d,0x3e,
-0x3f,0x3f,0x40,0x41,0x41,0x42,0x43,0x44,0x44,0x45,0x46,0x46,0x47,0x48,0x48,0x49,
-0x4a,0x4b,0x4b,0x4c,0x4d,0x4d,0x4e,0x4f,0x4f,0x50,0x51,0x52,0x52,0x53,0x54,0x54,
-0x55,0x56,0x56,0x57,0x58,0x59,0x59,0x5a,0x5b,0x5b,0x5c,0x5d,0x5d,0x5e,0x5f,0x60,
-0x60,0x61,0x62,0x62,0x63,0x64,0x64,0x65,0x66,0x67,0x67,0x68,0x69,0x69,0x6a,0x6b,
-0x6b,0x6c,0x6d,0x6e,0x6e,0x6f,0x70,0x70,0x71,0x72,0x72,0x73,0x74,0x75,0x75,0x76,
-0x77,0x77,0x78,0x79,0x79,0x7a,0x7b,0x7c,0x7c,0x7d,0x7e,0x7e,0x7f,0x80,0x80,0x81,
-0x82,0x83,0x83,0x84,0x85,0x85,0x86,0x87,0x87,0x88,0x89,0x8a,0x8a,0x8b,0x8c,0x8c,
-};
-
-// VDP transfer slots for blanked and active display in 32col and 40col mode.
-// 1 slot is 488/171 = 2.8538 68k cycles in h32, and 488/210 = 2.3238 in h40
-// In blanked display, all slots but 5(h32) / 6(h40) are usable for transfers,
-// in active display only 16(h32) / 18(h40) slots can be used.
-
-// NB the cyc2sl tables should cover 2 slot into the next scanline, in case the
-// last insn in the old scanline is a 32 bit VDP access. That is currently not
-// the case for active display.
-
-// XXX inactive tables by slot#=cycles*maxslot#/488. should be through hv tables
-// VDP transfer slots in inactive (blanked) display 32col mode.
-// refresh slots: 250, 26, 58, 90, 122 -> 32, 64, 96, 128, 160
-const unsigned char vdpcyc2sl_32_bl[] = { // 68k cycles/2 to slot #
-//  0   2   4   6   8  10  12  14  16  18  20  22  24  26  28  30
-    0,  0,  1,  2,  2,  3,  4,  4,  5,  6,  6,  7,  8,  8,  9, 10,
-   10, 11, 12, 12, 13, 14, 14, 15, 16, 17, 17, 18, 19, 19, 20, 21,
-   21, 22, 23, 23, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 31,
-   32, 33, 34, 34, 35, 36, 36, 37, 38, 38, 39, 40, 40, 41, 42, 42,
-   43, 44, 44, 45, 46, 46, 47, 48, 48, 49, 50, 51, 51, 52, 53, 53,
-   54, 55, 55, 56, 57, 57, 58, 59, 59, 60, 61, 61, 62, 63, 63, 64,
-   65, 65, 66, 67, 68, 68, 69, 70, 70, 71, 72, 72, 73, 74, 74, 75,
-   76, 76, 77, 78, 78, 79, 80, 80, 81, 82, 83, 83, 84, 85, 85, 86,
-   87, 87, 88, 89, 89, 90, 91, 91, 92, 93, 93, 94, 95, 95, 96, 97,
-   97, 98, 99,100,100,101,102,102,103,104,104,105,106,106,107,108,
-  108,109,110,110,111,112,112,113,114,114,115,116,117,117,118,119,
-  119,120,121,121,122,123,123,124,125,125,126,127,127,128,129,129,
-  130,131,131,132,133,134,134,135,136,136,137,138,138,139,140,140,
-  141,142,142,143,144,144,145,146,146,147,148,148,149,150,151,151,
-  152,153,153,154,155,155,156,157,157,158,159,159,160,161,161,162,
-  163,163,164,165,166,166,167,168,168,169,170,170,171,172,172,173,
-};
-// VDP transfer slots in inactive (blanked) display 40col mode.
-// refresh slots: 250, 26, 58, 90, 122, 154 -> 40, 72, 104, 136, 168, 200
-const unsigned char vdpcyc2sl_40_bl[] = { // 68k cycles/2 to slot #
-//  0   2   4   6   8  10  12  14  16  18  20  22  24  26  28  30
-    0,  0,  1,  2,  3,  4,  5,  5,  6,  7,  8,  9, 10, 10, 11, 12,
-   13, 14, 15, 15, 16, 17, 18, 19, 20, 20, 21, 22, 23, 24, 25, 25,
-   26, 27, 28, 29, 30, 30, 31, 32, 33, 34, 35, 35, 36, 37, 38, 39,
-   40, 40, 41, 42, 43, 44, 45, 45, 46, 47, 48, 49, 50, 51, 51, 52,
-   53, 54, 55, 56, 56, 57, 58, 59, 60, 61, 61, 62, 63, 64, 65, 66,
-   66, 67, 68, 69, 70, 71, 71, 72, 73, 74, 75, 76, 76, 77, 78, 79,
-   80, 81, 81, 82, 83, 84, 85, 86, 86, 87, 88, 89, 90, 91, 91, 92,
-   93, 94, 95, 96, 96, 97, 98, 99,100,101,102,102,103,104,105,106,
-  107,107,108,109,110,111,112,112,113,114,115,116,117,117,118,119,
-  120,121,122,122,123,124,125,126,127,127,128,129,130,131,132,132,
-  133,134,135,136,137,137,138,139,140,141,142,142,143,144,145,146,
-  147,147,148,149,150,151,152,153,153,154,155,156,157,158,158,159,
-  160,161,162,163,163,164,165,166,167,168,168,169,170,171,172,173,
-  173,174,175,176,177,178,178,179,180,181,182,183,183,184,185,186,
-  187,188,188,189,190,191,192,193,193,194,195,196,197,198,198,199,
-  200,201,202,203,204,204,205,206,207,208,209,209,210,211,212,213,
-};
-// VDP transfer slots in active display 32col mode. Transfer slots (Hint=0):
-// 11,25,40,48,56,72,80,88,104,112,120,136,144,152,167,168
-const unsigned char vdpcyc2sl_32[] = { // 68k cycles/2 to slot #
-//  0   2   4   6   8  10  12  14  16  18  20  22  24  26  28  30
-    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
-    1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
-    2,  2,  2,  2,  2,  2,  2,  2,  3,  3,  3,  3,  3,  3,  3,  3,
-    3,  3,  3,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  5,
-    5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,
-    5,  5,  5,  5,  5,  5,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,
-    6,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  8,  8,  8,
-    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
-    8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9, 10,
-   10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11,
-   11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
-   11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13,
-   13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14,
-   14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15,
-   16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
-};
-// VDP transfer slots in active display 40col mode. Transfer slots (Hint=0):
-// 21,47,55,63,79,87,95,111,119,127,143,151,159,175,183,191,206,207
-const unsigned char vdpcyc2sl_40[] = { // 68k cycles/2 to slot #
-//  0   2   4   6   8  10  12  14  16  18  20  22  24  26  28  30
-    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, //   0
-    0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,  1,  1,  1,  1, //  32
-    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, //  64
-    1,  1,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,  2,  2, //  96
-    3,  3,  3,  3,  3,  3,  3,  3,  3,  4,  4,  4,  4,  4,  4,  4, // 128
-    4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  5,  5,  5,  5, // 160
-    5,  5,  5,  5,  5,  6,  6,  6,  6,  6,  6,  6,  6,  6,  7,  7, // 192
-    7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7, // 224
-    7,  8,  8,  8,  8,  8,  8,  8,  8,  8,  9,  9,  9,  9,  9,  9, // 256
-    9,  9,  9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, // 288
-   10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, // 320
-   12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, // 352
-   13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, // 384
-   14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, // 416
-   16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, // 448
-   18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, // 480
-};
-
-// NB the sl2cyc tables must cover all slots present in the cyc2sl tables.
-
-// XXX inactive tables by cyc=slot#*488/maxslot#. should be through hv tables
-const unsigned short vdpsl2cyc_32_bl[] = { // slot # to 68k cycles/2
-    0,  2,  3,  5,  6,  8,  9, 11, 12, 14, 15, 17, 18, 20, 21, 23,
-   24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43, 45, 46,
-   48, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 64, 65, 67, 68, 70,
-   71, 73, 74, 75, 77, 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 93,
-   95, 96, 98, 99,100,102,103,105,106,108,109,111,112,114,115,117,
-  118,120,121,122,124,125,127,128,130,131,133,134,136,137,139,140,
-  142,143,145,146,147,149,150,152,153,155,156,158,159,161,162,164,
-  165,167,168,170,171,172,174,175,177,178,180,181,183,184,186,187,
-  189,190,192,193,195,196,197,199,200,202,203,205,206,208,209,211,
-  212,214,215,217,218,220,221,222,224,225,227,228,230,231,233,234,
-  236,237,239,240,242,243,244,246,247,249,250,252,253,255,256
-};
-const unsigned short vdpsl2cyc_40_bl[] = { // slot # to 68k cycles/2
-    0,  2,  3,  4,  5,  6,  8,  9, 10, 11, 12, 14, 15, 16, 17, 18,
-   20, 21, 22, 23, 24, 26, 27, 28, 29, 30, 32, 33, 34, 35, 36, 38,
-   39, 40, 41, 42, 44, 45, 46, 47, 48, 50, 51, 52, 53, 54, 56, 57,
-   58, 59, 60, 61, 63, 64, 65, 66, 67, 69, 70, 71, 72, 73, 75, 76,
-   77, 78, 79, 81, 82, 83, 84, 85, 87, 88, 89, 90, 91, 93, 94, 95,
-   96, 97, 99,100,101,102,103,105,106,107,108,109,111,112,113,114,
-  115,117,118,119,120,121,122,124,125,126,127,128,130,131,132,133,
-  134,136,137,138,139,140,142,143,144,145,146,148,149,150,151,152,
-  154,155,156,157,158,160,161,162,163,164,166,167,168,169,170,172,
-  173,174,175,176,178,179,180,181,182,183,185,186,187,188,189,191,
-  192,193,194,195,197,198,199,200,201,203,204,205,206,207,209,210,
-  211,212,213,215,216,217,218,219,221,222,223,224,225,227,228,229,
-  230,231,233,234,235,236,237,239,240,241,242,243,244,246,247,248,
-  249,250,252,253,254,255,257
-};
-const unsigned short vdpsl2cyc_32[] = { // slot # to 68k cycles/2
-    0, 16, 36, 56, 67, 79,102,113,125,148,159,171,194,205,217,239,
-  240,260,280
-};
-const unsigned short vdpsl2cyc_40[] = { // slot # to 68k cycles/2
-    0, 24, 55, 64, 73, 92,101,110,129,138,147,166,175,184,203,212,
-  221,239,240,268,299
-};
 
 #ifndef _ASM_MISC_C
 PICO_INTERNAL_ASM void memcpy16bswap(unsigned short *dest, void *src, int count)

+ 5 - 5
pico/patch.c

@@ -446,11 +446,11 @@ void PicoPatchPrepare(void)
       addr=PicoPatches[i].addr;
       addr &= ~1;
       if (addr < Pico.romsize)
-         PicoPatches[i].data_old = *(unsigned short *)(Pico.rom + addr);
+         PicoPatches[i].data_old = *(u16 *)(Pico.rom + addr);
       else
       {
          if(!(PicoIn.AHW & PAHW_SMS))
-            PicoPatches[i].data_old = (unsigned short) m68k_read16(addr);
+            PicoPatches[i].data_old = (u16) m68k_read16(addr);
          else
             ;// wrong: PicoPatches[i].data_old = (unsigned char) PicoRead8_z80(addr);
       }
@@ -473,7 +473,7 @@ void PicoPatchApply(void)
          if (PicoPatches[i].active)
          {
             if (!(PicoIn.AHW & PAHW_SMS))
-               *(unsigned short *)(Pico.rom + addr) = PicoPatches[i].data;
+               *(u16 *)(Pico.rom + addr) = PicoPatches[i].data;
             else if (!PicoPatches[i].comp || PicoPatches[i].comp == *(char *)(Pico.rom + addr))
                *(char *)(Pico.rom + addr) = (char) PicoPatches[i].data;
          }
@@ -485,13 +485,13 @@ void PicoPatchApply(void)
             if (u == i)
             {
                if (!(PicoIn.AHW & PAHW_SMS))
-                  *(unsigned short *)(Pico.rom + addr) = PicoPatches[i].data_old;
+                  *(u16 *)(Pico.rom + addr) = PicoPatches[i].data_old;
                else
                   *(char *)(Pico.rom + addr) = (char) PicoPatches[i].data_old;
             }
          }
       // fprintf(stderr, "patched %i: %06x:%04x\n", PicoPatches[i].active, addr,
-      // *(unsigned short *)(Pico.rom + addr));
+      // *(u16 *)(Pico.rom + addr));
       }
       else
       {

+ 1 - 0
pico/pico.c

@@ -40,6 +40,7 @@ void PicoInit(void)
   Pico32xInit();
   PsndInit();
 
+  PicoVideoInit();
   PicoDrawInit();
   PicoDraw2Init();
 }

+ 2 - 1
pico/pico_cmn.c

@@ -67,7 +67,8 @@ static void do_hint(struct PicoVideo *pv)
   pv->pending_ints |= 0x10;
   if (pv->reg[0] & 0x10) {
     elprintf(EL_INTS, "hint: @ %06x [%u]", SekPc, SekCyclesDone());
-    SekInterrupt(4);
+    if (SekIrqLevel < 4)
+      SekInterrupt(4);
   }
 }
 

+ 8 - 5
pico/pico_int.h

@@ -102,16 +102,16 @@ extern m68ki_cpu_core PicoCpuMM68k, PicoCpuMS68k;
 #define SekSr     m68k_get_reg(&PicoCpuMM68k, M68K_REG_SR)
 #define SekSrS68k m68k_get_reg(&PicoCpuMS68k, M68K_REG_SR)
 #define SekSetStop(x) { \
-	if(x) { SET_CYCLES(0); PicoCpuMM68k.stopped=STOP_LEVEL_STOP; } \
+	if(x) { PicoCpuMM68k.cyc_remaining_cycles = 0; PicoCpuMM68k.stopped=STOP_LEVEL_STOP; } \
 	else PicoCpuMM68k.stopped=0; \
 }
 #define SekSetStopS68k(x) { \
-	if(x) { SET_CYCLES(0); PicoCpuMS68k.stopped=STOP_LEVEL_STOP; } \
+	if(x) { PicoCpuMS68k.cyc_remaining_cycles = 0; PicoCpuMS68k.stopped=STOP_LEVEL_STOP; } \
 	else PicoCpuMS68k.stopped=0; \
 }
 #define SekIsStoppedM68k() (PicoCpuMM68k.stopped==STOP_LEVEL_STOP)
 #define SekIsStoppedS68k() (PicoCpuMS68k.stopped==STOP_LEVEL_STOP)
-#define SekShouldInterrupt() (CPU_INT_LEVEL > FLAG_INT_MASK)
+#define SekShouldInterrupt() (PicoCpuMM68k.int_level > PicoCpuMM68k.int_mask)
 
 #define SekNotPolling     PicoCpuMM68k.not_polling
 #define SekNotPollingS68k PicoCpuMS68k.not_polling
@@ -491,7 +491,8 @@ struct mcd_misc
   unsigned char  dmna_ret_2m;
   unsigned char  need_sync;
   unsigned char  pad3;
-  int pad4[9];
+  unsigned int   m68k_poll_clk;
+  int pad4[8];
 };
 
 typedef struct
@@ -773,6 +774,7 @@ void pcd_event_schedule(unsigned int now, enum pcd_event event, int after);
 void pcd_event_schedule_s68k(enum pcd_event event, int after);
 void pcd_prepare_frame(void);
 unsigned int pcd_cycles_m68k_to_s68k(unsigned int c);
+void pcd_irq_s68k(int irq, int state);
 int  pcd_sync_s68k(unsigned int m68k_target, int m68k_poll_sync);
 void pcd_run_cpus(int m68k_cycles);
 void pcd_soft_reset(void);
@@ -780,7 +782,7 @@ void pcd_state_loaded(void);
 
 // cd/pcm.c
 void pcd_pcm_sync(unsigned int to);
-void pcd_pcm_update(int *buffer, int length, int stereo);
+void pcd_pcm_update(s32 *buffer, int length, int stereo);
 void pcd_pcm_write(unsigned int a, unsigned int d);
 unsigned int pcd_pcm_read(unsigned int a);
 
@@ -876,6 +878,7 @@ void PicoVideoFIFOSync(int cycles);
 int PicoVideoFIFOHint(void);
 void PicoVideoFIFOMode(int active, int h40);
 int PicoVideoFIFOWrite(int count, int byte_p, unsigned sr_mask, unsigned sr_flags);
+void PicoVideoInit(void);
 void PicoVideoSave(void);
 void PicoVideoLoad(void);
 void PicoVideoCacheSAT(void);

+ 2 - 2
pico/pico_port.h

@@ -57,7 +57,7 @@
 #define MEM_LE2(a)	(a)
 #define MEM_LE4(a)	(a)
 // swapping
-#define CPU_BE2(v)	((u32)((u64)(v)<<16)|((v)>>16))
+#define CPU_BE2(v)	((u32)((u64)(v)<<16)|((u32)(v)>>16))
 #define CPU_BE4(v)	(((u32)(v)>>24)|(((v)>>8)&0x00ff00)| \
 			(((v)<<8)&0xff0000)|(u32)((v)<<24))
 #define CPU_LE2(v)	(v)		// swap of 2*u16 in u32
@@ -71,7 +71,7 @@
 // swapping
 #define CPU_BE2(v)	(v)
 #define CPU_BE4(v)	(v)
-#define CPU_LE2(v)	((u32)((u64)(v)<<16)|((v)>>16))
+#define CPU_LE2(v)	((u32)((u64)(v)<<16)|((u32)(v)>>16))
 #define CPU_LE4(v)	(((u32)(v)>>24)|(((v)>>8)&0x00ff00)| \
 			(((v)<<8)&0xff0000)|(u32)((v)<<24))
 #endif

+ 21 - 21
pico/sek.c

@@ -192,14 +192,14 @@ PICO_INTERNAL void SekSetRealTAS(int use_real)
 // XXX: rename
 PICO_INTERNAL void SekPackCpu(unsigned char *cpu, int is_sub)
 {
-  unsigned int pc=0;
+  u32 pc=0;
 
 #if defined(EMU_C68K)
   struct Cyclone *context = is_sub ? &PicoCpuCS68k : &PicoCpuCM68k;
   memcpy(cpu,context->d,0x40);
   pc=context->pc-context->membase;
-  *(unsigned int *)(cpu+0x44)=CycloneGetSr(context);
-  *(unsigned int *)(cpu+0x48)=context->osp;
+  *(u32 *)(cpu+0x44)=CycloneGetSr(context);
+  *(u32 *)(cpu+0x48)=context->osp;
   cpu[0x4c] = context->irq;
   cpu[0x4d] = context->state_flags & 1;
 #elif defined(EMU_M68K)
@@ -207,8 +207,8 @@ PICO_INTERNAL void SekPackCpu(unsigned char *cpu, int is_sub)
   m68k_set_context(is_sub ? &PicoCpuMS68k : &PicoCpuMM68k);
   memcpy(cpu,m68ki_cpu_p->dar,0x40);
   pc=m68ki_cpu_p->pc;
-  *(unsigned int  *)(cpu+0x44)=m68k_get_reg(NULL, M68K_REG_SR);
-  *(unsigned int  *)(cpu+0x48)=m68ki_cpu_p->sp[m68ki_cpu_p->s_flag^SFLAG_SET];
+  *(u32  *)(cpu+0x44)=m68k_get_reg(NULL, M68K_REG_SR);
+  *(u32  *)(cpu+0x48)=m68ki_cpu_p->sp[m68ki_cpu_p->s_flag^SFLAG_SET];
   cpu[0x4c] = CPU_INT_LEVEL>>8;
   cpu[0x4d] = CPU_STOPPED;
   m68k_set_context(oldcontext);
@@ -216,14 +216,14 @@ PICO_INTERNAL void SekPackCpu(unsigned char *cpu, int is_sub)
   M68K_CONTEXT *context = is_sub ? &PicoCpuFS68k : &PicoCpuFM68k;
   memcpy(cpu,context->dreg,0x40);
   pc=context->pc;
-  *(unsigned int  *)(cpu+0x44)=context->sr;
-  *(unsigned int  *)(cpu+0x48)=context->asp;
+  *(u32  *)(cpu+0x44)=context->sr;
+  *(u32  *)(cpu+0x48)=context->asp;
   cpu[0x4c] = context->interrupts[0];
   cpu[0x4d] = (context->execinfo & FM68K_HALTED) ? 1 : 0;
 #endif
 
-  *(unsigned int *)(cpu+0x40) = pc;
-  *(unsigned int *)(cpu+0x50) =
+  *(u32 *)(cpu+0x40) = pc;
+  *(u32 *)(cpu+0x50) =
     is_sub ? SekCycleCntS68k : Pico.t.m68c_cnt;
 }
 
@@ -231,11 +231,11 @@ PICO_INTERNAL void SekUnpackCpu(const unsigned char *cpu, int is_sub)
 {
 #if defined(EMU_C68K)
   struct Cyclone *context = is_sub ? &PicoCpuCS68k : &PicoCpuCM68k;
-  CycloneSetSr(context, *(unsigned int *)(cpu+0x44));
-  context->osp=*(unsigned int *)(cpu+0x48);
+  CycloneSetSr(context, *(u32 *)(cpu+0x44));
+  context->osp=*(u32 *)(cpu+0x48);
   memcpy(context->d,cpu,0x40);
   context->membase = 0;
-  context->pc = *(unsigned int *)(cpu+0x40);
+  context->pc = *(u32 *)(cpu+0x40);
   CycloneUnpack(context, NULL); // rebase PC
   context->irq = cpu[0x4c];
   context->state_flags = 0;
@@ -244,27 +244,27 @@ PICO_INTERNAL void SekUnpackCpu(const unsigned char *cpu, int is_sub)
 #elif defined(EMU_M68K)
   void *oldcontext = m68ki_cpu_p;
   m68k_set_context(is_sub ? &PicoCpuMS68k : &PicoCpuMM68k);
-  m68k_set_reg(M68K_REG_SR, *(unsigned int *)(cpu+0x44));
+  m68k_set_reg(M68K_REG_SR, *(u32 *)(cpu+0x44));
   memcpy(m68ki_cpu_p->dar,cpu,0x40);
-  m68ki_cpu_p->pc=*(unsigned int *)(cpu+0x40);
-  m68ki_cpu_p->sp[m68ki_cpu_p->s_flag^SFLAG_SET]=*(unsigned int *)(cpu+0x48);
+  m68ki_cpu_p->pc=*(u32 *)(cpu+0x40);
+  m68ki_cpu_p->sp[m68ki_cpu_p->s_flag^SFLAG_SET]=*(u32 *)(cpu+0x48);
   CPU_INT_LEVEL = cpu[0x4c] << 8;
   CPU_STOPPED = cpu[0x4d];
   m68k_set_context(oldcontext);
 #elif defined(EMU_F68K)
   M68K_CONTEXT *context = is_sub ? &PicoCpuFS68k : &PicoCpuFM68k;
   memcpy(context->dreg,cpu,0x40);
-  context->pc =*(unsigned int *)(cpu+0x40);
-  context->sr =*(unsigned int *)(cpu+0x44);
-  context->asp=*(unsigned int *)(cpu+0x48);
+  context->pc =*(u32 *)(cpu+0x40);
+  context->sr =*(u32 *)(cpu+0x44);
+  context->asp=*(u32 *)(cpu+0x48);
   context->interrupts[0] = cpu[0x4c];
   context->execinfo &= ~FM68K_HALTED;
   if (cpu[0x4d]&1) context->execinfo |= FM68K_HALTED;
 #endif
   if (is_sub)
-    SekCycleCntS68k = *(unsigned int *)(cpu+0x50);
+    SekCycleCntS68k = *(u32 *)(cpu+0x50);
   else
-    Pico.t.m68c_cnt = *(unsigned int *)(cpu+0x50);
+    Pico.t.m68c_cnt = *(u32 *)(cpu+0x50);
 }
 
 
@@ -398,7 +398,7 @@ int SekRegisterIdlePatch(unsigned int pc, int oldop, int newop, void *ctx)
 
   // XXX: probably shouldn't patch RAM too
   v = m68k_read16_map[pc >> M68K_MEM_SHIFT];
-  if (!(v & 0x80000000))
+  if (~v & ~((uptr)-1LL >> 1)) // MSB clear?
     target = (u16 *)((v << 1) + pc);
   else {
     if (++idledet_bads > 128)

+ 2 - 2
pico/sound/mix_arm.S

@@ -156,7 +156,7 @@ m16_32_s2_no_unal2:
 @ limit
 @ reg=int_sample, r12=1, r8=tmp, kills flags
 .macro Limit reg
-    sub     \reg, \reg, \reg, asr #2	@ reduce audio lvl some to avoid clipping
+@   sub     \reg, \reg, \reg, asr #2	@ reduce audio lvl to avoid clipping
     add     r8, r12, \reg, asr #15
     bics    r8, r8, #1			@ in non-overflow conditions r8 is 0 or 1
     movne   \reg, #0x8000
@@ -166,7 +166,7 @@ m16_32_s2_no_unal2:
 @ limit and shift up by 16
 @ reg=int_sample, r12=1, r8=tmp, kills flags
 .macro Limitsh reg
-    sub     \reg, \reg, \reg, asr #2	@ reduce audio lvl some to avoid clipping
+@   sub     \reg, \reg, \reg, asr #2	@ reduce audio lvl to avoid clipping
     add     r8, r12,\reg, asr #15
     bics    r8, r8, #1			@ in non-overflow conditions r8 is 0 or 1
     moveq   \reg, \reg, lsl #16

+ 3 - 3
pico/sound/sound.c

@@ -17,10 +17,10 @@
 void (*PsndMix_32_to_16l)(short *dest, int *src, int count) = mix_32_to_16l_stereo;
 
 // master int buffer to mix to
-static int PsndBuffer[2*(44100+100)/50];
+static s32 PsndBuffer[2*(44100+100)/50];
 
 // cdda output buffer
-short cdda_out_buffer[2*1152];
+s16 cdda_out_buffer[2*1152];
 
 // sn76496
 extern int *sn76496_regs;
@@ -236,7 +236,7 @@ PICO_INTERNAL void PsndDoFM(int cyc_to)
   // Q16, number of samples since last call
   len = (cyc_to * Pico.snd.clkl_mult) - Pico.snd.fm_pos;
 
-  // don't do this too often (about once every canline)
+  // don't do this too often (about once every scanline)
   if (len >> 16 <= PicoIn.sndRate >> 10)
     return;
 

+ 2 - 0
pico/sound/ym2612.c

@@ -2185,6 +2185,7 @@ int YM2612PicoStateLoad2(int *tat, int *tbt)
 		blk = ym2612.CH[c].block_fnum >> 11;
 		ym2612.CH[c].kcode= (blk<<2) | opn_fktable[fn >> 7];
 		ym2612.CH[c].fc = fn_table[fn*2]>>(7-blk);
+		refresh_fc_eg_chan( &ym2612.CH[c] );
 	}
 	// chans 4,5,6
 	ptr = &ym2612.REGS[0x1b8];
@@ -2205,6 +2206,7 @@ int YM2612PicoStateLoad2(int *tat, int *tbt)
 		blk = ym2612.CH[c].block_fnum >> 11;
 		ym2612.CH[c].kcode= (blk<<2) | opn_fktable[fn >> 7];
 		ym2612.CH[c].fc = fn_table[fn*2]>>(7-blk);
+		refresh_fc_eg_chan( &ym2612.CH[c] );
 	}
 	for (c = 0; c < 3; c++)
 	{

+ 150 - 44
pico/videoport.c

@@ -2,7 +2,7 @@
  * PicoDrive
  * (c) Copyright Dave, 2004
  * (C) notaz, 2006-2009
- * (C) kub, 2020
+ * (C) kub, 2020,2021
  *
  * This work is licensed under the terms of MAME license.
  * See COPYING file in the top-level directory.
@@ -12,11 +12,131 @@
 #define NEED_DMA_SOURCE
 #include "memory.h"
 
-extern const unsigned char  hcounts_32[], hcounts_40[];
-extern const unsigned char  vdpcyc2sl_32_bl[], vdpcyc2sl_40_bl[];
-extern const unsigned char  vdpcyc2sl_32[], vdpcyc2sl_40[];
-extern const unsigned short vdpsl2cyc_32_bl[], vdpsl2cyc_40_bl[];
-extern const unsigned short vdpsl2cyc_32[], vdpsl2cyc_40[];
+
+enum { clkdiv = 2 };    // CPU clock granularity: one of 1,2,4,8
+
+// VDP Slot timing, taken from http://gendev.spritesmind.net/
+//     forum/viewtopic.php?f=22&t=851&sid=d5701a71396ee7f700c74fb7cd85cb09
+// Thank you very much for the great work, Nemesis!
+
+// Slot clock is sysclock/20 for h32 and sysclock/16 for h40.
+// One scanline is 63.7us/63.5us (h32/h40) long which is 488.6/487.4 68k cycles.
+// Assume 488 for everything.
+// 1 slot is 488/171 = 2.8538 68k cycles in h32, and 488/210 = 2.3238 in h40.
+enum { slcpu = 488 };
+
+// VDP has a slot counter running from 0x00 to 0xff every scanline, but it has
+// a gap depending on the video mode. The slot in which a horizontal interrupt
+// is generated also depends on the video mode.
+enum { hint32 = 0x85, gapstart32 = 0x94, gapend32 = 0xe9};
+enum { hint40 = 0xa5, gapstart40 = 0xb7, gapend40 = 0xe5};
+
+// The horizontal sync period (HBLANK) is 30/37 slots (h32/h40):
+// h32: 4 slots front porch (1.49us), 13 HSYNC (4.84us), 13 back porch (4.84us)
+// h40: 5 slots front porch (1.49us), 16 HSYNC (4.77us), 16 back porch (4.77us)
+// HBLANK starts in slot 0x93/0xb4, according to Nemesis' measurements.
+enum { hboff32 = 0x93-hint32, hblen32 = 0xf8-(gapend32-gapstart32)-hint32};//30
+enum { hboff40 = 0xb4-hint40, hblen40 = 0xf8-(gapend40-gapstart40)-hint40};//37
+
+// number of slots in a scanline
+#define slots32	(0x100-(gapend32-gapstart32)) // 171
+#define slots40	(0x100-(gapend40-gapstart40)) // 210
+
+// In blanked display, all slots but the refresh slots are usable for transfers,
+// in active display only 16(h32) / 18(h40) slots can be used.
+
+// dma and refresh slots for active display, 16 for H32
+static u8 dmaslots32[] =
+    { 145,243, 2,10,18, 34,42,50, 66,74,82, 98,106,114, 129,130 };
+static u8 refslots32[] =
+    {        250,     26,       58,       90,         122 };
+// dma and refresh slots for active display, 18 for H40
+static u8 dmaslots40[] =
+    {     232, 2,10,18, 34,42,50, 66,74,82, 98,106,114, 130,138,146, 161,162 };
+static u8 refslots40[] =
+    {        250,     26,       58,       90,         122,         154 };
+
+// table sizes
+enum { cycsz = slcpu/clkdiv };
+enum { sl32blsz=slots32-sizeof(refslots32)+1, sl32acsz=sizeof(dmaslots32)+1 };
+enum { sl40blsz=slots40-sizeof(refslots40)+1, sl40acsz=sizeof(dmaslots40)+1 };
+
+// Tables must be considerably larger than one scanline, since 68k emulation
+// isn't stopping in the middle of an operation. If the last op is a 32 bit
+// VDP access 2 slots may need to be taken from the next scanline, which can be
+// more than 100 CPU cycles. For safety just cover 2 scanlines.
+
+// table for hvcounter mapping. check: Sonic 3D Blast bonus, Cannon Fodder,
+// Chase HQ II, 3 Ninjas kick back, Road Rash 3, Skitchin', Wheel of Fortune
+static u8  hcounts_32[2*cycsz], hcounts_40[2*cycsz];
+// tables mapping cycles to slots
+static u16 vdpcyc2sl_32_bl[2*cycsz],vdpcyc2sl_40_bl[2*cycsz];
+static u16 vdpcyc2sl_32_ac[2*cycsz],vdpcyc2sl_40_ac[2*cycsz];
+// tables mapping slots to cycles
+// NB the sl2cyc tables must cover all slots present in the cyc2sl tables.
+static u16 vdpsl2cyc_32_bl[2*sl32blsz],vdpsl2cyc_40_bl[2*sl40blsz];
+static u16 vdpsl2cyc_32_ac[2*sl32acsz],vdpsl2cyc_40_ac[2*sl40acsz];
+
+
+// calculate timing tables for one mode (H32 or H40)
+// NB tables aligned to HINT, since the main loop uses HINT as synchronization
+#define INITTABLES(s) { \
+  float factor = (float)slcpu/slots##s;					\
+  int ax, bx, rx, ac, bc;						\
+  int i, n;								\
+									\
+  /* calculate internal VDP slot numbers */				\
+  for (i = 0; i < cycsz; i++) {						\
+    n = hint##s + i*clkdiv/factor;					\
+    if (n >= gapstart##s) n += gapend##s-gapstart##s;			\
+    hcounts_##s[i] = n % 256;						\
+  }									\
+									\
+  ax = bx = ac = bc = rx = 0;						\
+  for (i = 0; i < cycsz; i++) {						\
+    n = hcounts_##s[i];							\
+    if (i == 0 || n != hcounts_##s[i-1]) {				\
+      /* fill slt <=> cycle tables, active scanline */			\
+      if (ax < ARRAY_SIZE(dmaslots##s) && dmaslots##s[ax] == n) {	\
+        vdpsl2cyc_##s##_ac[++ax]=i;					\
+        while (ac < i) vdpcyc2sl_##s##_ac[ac++] = ax-1;			\
+      }									\
+      /* fill slt <=> cycle tables, scanline off */			\
+      if (rx >= ARRAY_SIZE(refslots##s) || refslots##s[rx] != n) {	\
+        vdpsl2cyc_##s##_bl[++bx]=i;					\
+        while (bc < i) vdpcyc2sl_##s##_bl[bc++] = bx-1;			\
+      } else								\
+        ++rx;								\
+    }									\
+  }									\
+  /* fill up cycle to slot mappings for last slot */			\
+  while (ac < cycsz)							\
+    vdpcyc2sl_##s##_ac[ac] = ARRAY_SIZE(dmaslots##s),		ac++;	\
+  while (bc < cycsz)							\
+    vdpcyc2sl_##s##_bl[bc] = slots##s-ARRAY_SIZE(refslots##s),	bc++;	\
+									\
+  /* extend tables for 2nd scanline */					\
+  memcpy(hcounts_##s+cycsz, hcounts_##s, ARRAY_SIZE(hcounts_##s)-cycsz);\
+  i = ARRAY_SIZE(dmaslots##s);						\
+  while (ac < ARRAY_SIZE(vdpcyc2sl_##s##_ac))				\
+    vdpcyc2sl_##s##_ac[ac] = vdpcyc2sl_##s##_ac[ac-cycsz]+i,	ac++;	\
+  while (ax < ARRAY_SIZE(vdpsl2cyc_##s##_ac)-1)			ax++,	\
+    vdpsl2cyc_##s##_ac[ax] = vdpsl2cyc_##s##_ac[ax-i]+cycsz;		\
+  i = slots##s - ARRAY_SIZE(refslots##s);				\
+  while (bc < ARRAY_SIZE(vdpcyc2sl_##s##_bl))				\
+    vdpcyc2sl_##s##_bl[bc] = vdpcyc2sl_##s##_bl[bc-cycsz]+i,	bc++;	\
+  while (bx < ARRAY_SIZE(vdpsl2cyc_##s##_bl)-1)			bx++,	\
+    vdpsl2cyc_##s##_bl[bx] = vdpsl2cyc_##s##_bl[bx-i]+cycsz;		\
+}
+ 
+
+// initialize VDP timing tables
+void PicoVideoInit(void)
+{
+  INITTABLES(32);
+  INITTABLES(40);
+}
+
 
 static int blankline;           // display disabled for this line
 
@@ -69,16 +189,17 @@ static struct VdpFIFO { // XXX this must go into save file!
   unsigned short fifo_slot;   // last executed slot in current scanline
   unsigned short fifo_maxslot;// #slots in scanline
 
-  const unsigned char *fifo_cyc2sl;
+  const unsigned short *fifo_cyc2sl;
   const unsigned short *fifo_sl2cyc;
 } VdpFIFO;
 
 enum { FQ_BYTE = 1, FQ_BGDMA = 2, FQ_FGDMA = 4 }; // queue flags, NB: BYTE = 1!
 
-// NB must limit cyc2sl to table size in case 68k overdraws its aim. That can
-// happen if the last insn is a blocking acess to VDP, or for exceptions (e.g.irq)
-#define	Cyc2Sl(vf,lc)	((lc) < 256*2 ? vf->fifo_cyc2sl[(lc)>>1] : vf->fifo_cyc2sl[255])
-#define Sl2Cyc(vf,sl)   (vf->fifo_sl2cyc[sl]*2)
+
+// NB should limit cyc2sl to table size in case 68k overdraws its aim. That can
+// happen if the last op is a blocking acess to VDP, or for exceptions (e.g.irq)
+#define	Cyc2Sl(vf,lc)	(vf->fifo_cyc2sl[(lc)/clkdiv])
+#define Sl2Cyc(vf,sl)   (vf->fifo_sl2cyc[sl]*clkdiv)
 
 // do the FIFO math
 static __inline int AdvanceFIFOEntry(struct VdpFIFO *vf, struct PicoVideo *pv, int slots)
@@ -90,7 +211,8 @@ static __inline int AdvanceFIFOEntry(struct VdpFIFO *vf, struct PicoVideo *pv, i
   if (l > cnt)
     l = cnt;
   if (!(vf->fifo_queue[vf->fifo_qx] & FQ_BGDMA))
-    if ((vf->fifo_total -= ((cnt & b) + l) >> b) < 0) vf->fifo_total = 0;
+    vf->fifo_total -= ((cnt & b) + l) >> b;
+  if (vf->fifo_total < 0) vf->fifo_total = 0;
   cnt -= l;
 
   // if entry has been processed...
@@ -263,7 +385,7 @@ int PicoVideoFIFOWrite(int count, int flags, unsigned sr_mask,unsigned sr_flags)
 
     // update FIFO state if it was empty
     if (!(pv->status & PVS_FIFORUN)) {
-      vf->fifo_slot = Cyc2Sl(vf, lc+9); // FIFO latency ~3 vdp slots
+      vf->fifo_slot = Cyc2Sl(vf, lc+7); // FIFO latency ~3 vdp slots
       pv->status |= PVS_FIFORUN;
       pv->fifo_cnt = count << (flags & FQ_BYTE);
     }
@@ -300,10 +422,10 @@ int PicoVideoFIFOHint(void)
 // switch FIFO mode between active/inactive display
 void PicoVideoFIFOMode(int active, int h40)
 {
-  static const unsigned char *vdpcyc2sl[2][2] =
-        { {vdpcyc2sl_32_bl, vdpcyc2sl_40_bl} , {vdpcyc2sl_32, vdpcyc2sl_40} };
+  static const unsigned short *vdpcyc2sl[2][2] =
+      { {vdpcyc2sl_32_bl, vdpcyc2sl_40_bl},{vdpcyc2sl_32_ac, vdpcyc2sl_40_ac} };
   static const unsigned short *vdpsl2cyc[2][2] =
-        { {vdpsl2cyc_32_bl, vdpsl2cyc_40_bl} , {vdpsl2cyc_32, vdpsl2cyc_40} };
+      { {vdpsl2cyc_32_bl, vdpsl2cyc_40_bl},{vdpsl2cyc_32_ac, vdpsl2cyc_40_ac} };
 
   struct VdpFIFO *vf = &VdpFIFO;
   struct PicoVideo *pv = &Pico.video;
@@ -316,11 +438,10 @@ void PicoVideoFIFOMode(int active, int h40)
   vf->fifo_cyc2sl = vdpcyc2sl[active][h40];
   vf->fifo_sl2cyc = vdpsl2cyc[active][h40];
   // recalculate FIFO slot for new mode
-  vf->fifo_slot = Cyc2Sl(vf, lc)-1;
+  vf->fifo_slot = Cyc2Sl(vf, lc);
   vf->fifo_maxslot = Cyc2Sl(vf, 488);
 }
 
-
 // VDP memory rd/wr
 
 static __inline void AutoIncrement(void)
@@ -722,8 +843,8 @@ PICO_INTERNAL_ASM void PicoVideoWrite(u32 a,unsigned short d)
   case 0x00: // Data port 0 or 2
     // try avoiding the sync..
     if (Pico.m.scanline < (pvid->reg[1]&0x08 ? 240 : 224) && (pvid->reg[1]&0x40) &&
-        !(!pvid->pending &&
-          ((pvid->command & 0xc00000f0) == 0x40000010 && PicoMem.vsram[pvid->addr>>1] == (d & 0x7ff)))
+        !(!pvid->pending && ((pvid->command & 0xc00000f0) == 0x40000010 &&
+                        PicoMem.vsram[(pvid->addr>>1) & 0x3f] == (d & 0x7ff)))
        )
       DrawSync(0); // XXX  it's unclear when vscroll data is fetched from vsram?
 
@@ -881,10 +1002,10 @@ update_irq:
 
 static u32 VideoSr(const struct PicoVideo *pv)
 {
-  unsigned int hp = pv->reg[12]&1 ? 15*488/210+1 : 15*488/171+1; // HBLANK start
-  unsigned int hl = pv->reg[12]&1 ? 37*488/210+1 : 28*488/171+1; // HBLANK len
+  unsigned int hp = pv->reg[12]&1 ? hboff40*488/slots40 : hboff32*488/slots32;
+  unsigned int hl = pv->reg[12]&1 ? hblen40*488/slots40 : hblen32*488/slots32;
   unsigned int c;
-  u32 d = pv->status;
+  u32 d = (u16)pv->status;
 
   c = SekCyclesDone() - Pico.t.m68c_line_start;
   if (c - hp < hl)
@@ -914,32 +1035,17 @@ PICO_INTERNAL_ASM u32 PicoVideoRead(u32 a)
     return d;
   }
 
-  // H-counter info (based on Generator):
-  // frame:
-  //                       |       <- hblank? ->      |
-  // start    <416>       hint  <36> hdisplay <38>  end // CPU cycles
-  // |---------...---------|------------|-------------|
-  // 0                   B6 E4                       FF // 40 cells
-  // 0                   93 E8                       FF // 32 cells
-
-  // Gens (?)              v-render
-  // start  <hblank=84>   hint    hdisplay <404>      |
-  // |---------------------|--------------------------|
-  // E4  (hc[0x43]==0)    07                         B1 // 40
-  // E8  (hc[0x45]==0)    05                         91 // 32
-
-  // check: Sonic 3D Blast bonus, Cannon Fodder, Chase HQ II, 3 Ninjas kick back, Road Rash 3, Skitchin', Wheel of Fortune
   if ((a&0x1c)==0x08)
   {
     unsigned int c;
     u32 d;
 
-    c = (SekCyclesDone() - Pico.t.m68c_line_start) & 0x1ff; // FIXME
+    c = SekCyclesDone() - Pico.t.m68c_line_start;
     if (Pico.video.reg[0]&2)
          d = Pico.video.hv_latch;
     else if (Pico.video.reg[12]&1)
-         d = hcounts_40[c/2] | (Pico.video.v_counter << 8);
-    else d = hcounts_32[c/2] | (Pico.video.v_counter << 8);
+         d = hcounts_40[c/clkdiv] | (Pico.video.v_counter << 8);
+    else d = hcounts_32[c/clkdiv] | (Pico.video.v_counter << 8);
 
     elprintf(EL_HVCNT, "hv: %02x %02x [%u] @ %06x", d, Pico.video.v_counter, SekCyclesDone(), SekPc);
     return d;
@@ -996,12 +1102,12 @@ unsigned char PicoVideoRead8HV_H(int is_from_z80)
 // FIXME: broken
 unsigned char PicoVideoRead8HV_L(int is_from_z80)
 {
-  u32 d = (SekCyclesDone() - Pico.t.m68c_line_start) & 0x1ff; // FIXME
+  u32 d = SekCyclesDone() - Pico.t.m68c_line_start;
   if (Pico.video.reg[0]&2)
        d = Pico.video.hv_latch;
   else if (Pico.video.reg[12]&1)
-       d = hcounts_40[d/2];
-  else d = hcounts_32[d/2];
+       d = hcounts_40[d/clkdiv];
+  else d = hcounts_32[d/clkdiv];
   elprintf(EL_HVCNT, "hcounter: %02x [%u] @ %06x", d, SekCyclesDone(), SekPc);
   return d;
 }

+ 1 - 1
platform/common/config_file.c

@@ -280,7 +280,7 @@ static int custom_read(menu_entry *me, const char *var, const char *val)
 			return 1;
 
 		case MA_OPT_SOUND_ALPHA:
-			if (strcasecmp(var, "Filter strength (alpha)") != 0) return 0;
+			if (strcasecmp(var, "Filter strength") != 0) return 0;
 			PicoIn.sndFilterAlpha = 0x10000 * atof(val);
 			return 1;
 

+ 1 - 0
platform/common/dr_libs

@@ -0,0 +1 @@
+Subproject commit b20f7512a69b732ab4082c05e01fc29d42333fae

+ 1922 - 1922
platform/common/emu.c

@@ -1,1922 +1,1922 @@
-/*
- * PicoDrive
- * (C) notaz, 2007-2010
- *
- * This work is licensed under the terms of MAME license.
- * See COPYING file in the top-level directory.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#ifdef __GP2X__
-#include <unistd.h>
-#endif
-
-#include "../libpicofe/posix.h"
-#include "../libpicofe/input.h"
-#include "../libpicofe/fonts.h"
-#include "../libpicofe/sndout.h"
-#include "../libpicofe/lprintf.h"
-#include "../libpicofe/plat.h"
-#include "emu.h"
-#include "configfile_fk.h"
-#include "input_pico.h"
-#include "menu_pico.h"
-#include "config_file.h"
-
-#include <pico/pico_int.h>
-#include <pico/patch.h>
-
-#ifdef USE_LIBRETRO_VFS
-#include "file_stream_transforms.h"
-#endif
-
-#if defined(__GNUC__) && __GNUC__ >= 7
-#pragma GCC diagnostic ignored "-Wformat-truncation"
-#endif
-
-#ifndef _WIN32
-#define PATH_SEP      "/"
-#define PATH_SEP_C    '/'
-#else
-#define PATH_SEP      "\\"
-#define PATH_SEP_C    '\\'
-#endif
-
-#define STATUS_MSG_TIMEOUT 2000
-
-void *g_screen_ptr;
-
-int g_screen_width  = 320;
-int g_screen_height = 240;
-int g_screen_ppitch = 320; // pitch in pixels
-
-const char *PicoConfigFile = "config2.cfg";
-currentConfig_t currentConfig, defaultConfig;
-int state_slot = 0;
-int config_slot = 0, config_slot_current = 0;
-int pico_pen_x = 320/2, pico_pen_y = 240/2;
-int pico_inp_mode;
-int flip_after_sync;
-int engineState = PGS_Menu;
-int show_fps_bypass = 0;
-int need_screen_cleared = 0;
-
-static short __attribute__((aligned(4))) sndBuffer[2*44100/50];
-
-/* tmp buff to reduce stack usage for plats with small stack */
-static char static_buff[1024];
-const char *rom_fname_reload;
-char rom_fname_loaded[1024];
-int reset_timing = 0;
-static unsigned int notice_msg_time;	/* when started showing */
-static char noticeMsg[40];
-
-unsigned char *movie_data = NULL;
-static int movie_size = 0;
-
-
-/* don't use tolower() for easy old glibc binary compatibility */
-static void strlwr_(char *string)
-{
-	char *p;
-	for (p = string; *p; p++)
-		if ('A' <= *p && *p <= 'Z')
-			*p += 'a' - 'A';
-}
-
-static int try_rfn_cut(char *fname)
-{
-	FILE *tmp;
-	char *p;
-
-	p = fname + strlen(fname) - 1;
-	for (; p > fname; p--)
-		if (*p == '.') break;
-	*p = 0;
-
-	if((tmp = fopen(fname, "rb"))) {
-		fclose(tmp);
-		return 1;
-	}
-	return 0;
-}
-
-static void get_ext(const char *file, char *ext)
-{
-	const char *p;
-
-	p = file + strlen(file) - 4;
-	if (p < file) p = file;
-	strncpy(ext, p, 4);
-	ext[4] = 0;
-	strlwr_(ext);
-}
-
-static void fname_ext(char *dst, int dstlen, const char *prefix, const char *ext, const char *fname)
-{
-	int prefix_len = 0;
-	const char *p;
-
-	*dst = 0;
-	if (prefix) {
-		/*int len = plat_get_root_dir(dst, dstlen);
-		strcpy(dst + len, prefix);
-		prefix_len = len + strlen(prefix);*/
-
-		/* Saves are in ROM folder */
-		prefix_len = strlen(mRomPath)+1;
-		sprintf(dst, "%s/", mRomPath);
-	}
-
-	p = fname + strlen(fname) - 1;
-	for (; p >= fname && *p != PATH_SEP_C; p--)
-		;
-	p++;
-	strncpy(dst + prefix_len, p, dstlen - prefix_len - 1);
-
-	dst[dstlen - 8] = 0;
-	if ((p = strrchr(dst, '.')) != NULL)
-		dst[p-dst] = 0;
-	if (ext)
-		strcat(dst, ext);
-}
-
-static void romfname_ext(char *dst, int dstlen, const char *prefix, const char *ext)
-{
-	fname_ext(dst, dstlen, prefix, ext, rom_fname_loaded);
-}
-
-void emu_status_msg(const char *format, ...)
-{
-	va_list vl;
-	int ret;
-
-	va_start(vl, format);
-	ret = vsnprintf(noticeMsg, sizeof(noticeMsg), format, vl);
-	va_end(vl);
-
-	/* be sure old text gets overwritten */
-	for (; ret < 28; ret++)
-		noticeMsg[ret] = ' ';
-	noticeMsg[ret] = 0;
-
-	notice_msg_time = plat_get_ticks_ms();
-}
-
-static const char * const biosfiles_us[] = {
-	"us_scd2_9306", "SegaCDBIOS9303", "us_scd1_9210", "bios_CD_U"
-};
-static const char * const biosfiles_eu[] = {
-	"eu_mcd2_9306", "eu_mcd2_9303", "eu_mcd1_9210", "bios_CD_E"
-};
-static const char * const biosfiles_jp[] = {
-	"jp_mcd2_921222", "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J"
-};
-
-static const char *find_bios(int *region, const char *cd_fname)
-{
-	int i, count;
-	const char * const *files;
-	FILE *f = NULL;
-	int ret;
-
-	// we need to have config loaded at this point
-	ret = emu_read_config(cd_fname, 0);
-	if (!ret) emu_read_config(NULL, 0);
-
-	if (PicoIn.regionOverride) {
-		*region = PicoIn.regionOverride;
-		lprintf("override region to %s\n", *region != 4 ?
-			(*region == 8 ? "EU" : "JAP") : "USA");
-	}
-
-	if (*region == 4) { // US
-		files = biosfiles_us;
-		count = sizeof(biosfiles_us) / sizeof(char *);
-	} else if (*region == 8) { // EU
-		files = biosfiles_eu;
-		count = sizeof(biosfiles_eu) / sizeof(char *);
-	} else if (*region == 1 || *region == 2) {
-		files = biosfiles_jp;
-		count = sizeof(biosfiles_jp) / sizeof(char *);
-	} else {
-		return 0;
-	}
-
-	/** Check Bios files in home directory */
-	for (i = 0; i < count; i++)
-	{
-		emu_make_path(static_buff, files[i], sizeof(static_buff) - 4);
-		//printf("bios name static_buff: %s\n", static_buff);
-		strcat(static_buff, ".bin");
-		f = fopen(static_buff, "rb");
-		if (f) break;
-
-		static_buff[strlen(static_buff) - 4] = 0;
-		strcat(static_buff, ".zip");
-		f = fopen(static_buff, "rb");
-		if (f) break;
-	}
-
-	/** Check Bios files in <rom path>/bios */
-	if(!f){
-		for (i = 0; i < count; i++)
-		{
-			sprintf(static_buff, "%s/bios/%s", mRomPath, files[i]);
-			printf("static_buff: %s\n", static_buff);
-			strcat(static_buff, ".bin");
-			f = fopen(static_buff, "rb");
-			if (f) break;
-
-			static_buff[strlen(static_buff) - 4] = 0;
-			strcat(static_buff, ".zip");
-			f = fopen(static_buff, "rb");
-			if (f) break;
-		}
-	}
-
-	/** Check Bios files in rom path */
-	if(!f){
-		for (i = 0; i < count; i++)
-		{
-			sprintf(static_buff, "%s/%s", mRomPath, files[i]);
-			printf("static_buff: %s\n", static_buff);
-			strcat(static_buff, ".bin");
-			f = fopen(static_buff, "rb");
-			if (f) break;
-
-			static_buff[strlen(static_buff) - 4] = 0;
-			strcat(static_buff, ".zip");
-			f = fopen(static_buff, "rb");
-			if (f) break;
-		}
-	}
-
-	if (f) {
-		lprintf("using bios: %s\n", static_buff);
-		fclose(f);
-		return static_buff;
-	} else {
-		char* country;
-		char** bios_names;
-		if(*region == 4){ //USA
-			country = "USA";
-			bios_names = biosfiles_us;
-		}
-		else if(*region == 8){ //EU
-			country = "EU";
-			bios_names = biosfiles_eu;
-		}
-		else{ //JAP
-			country = "JAP";
-			bios_names = biosfiles_jp;
-		}
-
-		sprintf(static_buff, "no %s BIOS files found, read docs", country);
-		menu_update_msg(static_buff);
-
-		/** Set notif for BIOS */
-	    char shell_cmd[400];
-	    sprintf(shell_cmd, "%s 0 \"    %s BIOS FILE MISSING^^Connect your FunKey S to ^your computer and copy the^BIOS file in the folder:^    Sega Genesis/bios/^^The file can be called:^ - %s.bin^ - %s.bin^ - %s.bin^ - %s.bin^^For more instructions:^www.funkey-project.com^^Press any button to exit...\"",
-	            SHELL_CMD_NOTIF_SET, country, bios_names[0], bios_names[1], bios_names[2], bios_names[3]);
-	    FILE *fp = popen(shell_cmd, "r");
-	    if (fp == NULL) {
-	        printf("In %s, Failed to run command %s\n", __func__, shell_cmd);
-	    } else {
-	        pclose(fp);
-	    }
-
-	    /** Wait for key press */
-	    SDL_Event event;
-	    while(event.type != SDL_KEYUP && event.type != SDL_QUIT){
-	        while (SDL_PollEvent(&event))
-			SDL_Delay(60);
-	    }
-
-	    /** Clear notif for BIOS */
-	    fp = popen(SHELL_CMD_NOTIF_CLEAR, "r");
-	    if (fp == NULL) {
-	        printf("In %s, Failed to run command %s\n", __func__, SHELL_CMD_NOTIF_CLEAR);
-	    } else {
-	        pclose(fp);
-	    }
-
-	    /** Force clean exit */
-        //engineState = PGS_Quit;
-		emu_finish();
-		plat_finish();
-		plat_target_finish();
-		exit(0);
-		return NULL;
-	}
-}
-
-/* check if the name begins with BIOS name */
-/*
-static int emu_isBios(const char *name)
-{
-	int i;
-	for (i = 0; i < sizeof(biosfiles_us)/sizeof(biosfiles_us[0]); i++)
-		if (strstr(name, biosfiles_us[i]) != NULL) return 1;
-	for (i = 0; i < sizeof(biosfiles_eu)/sizeof(biosfiles_eu[0]); i++)
-		if (strstr(name, biosfiles_eu[i]) != NULL) return 1;
-	for (i = 0; i < sizeof(biosfiles_jp)/sizeof(biosfiles_jp[0]); i++)
-		if (strstr(name, biosfiles_jp[i]) != NULL) return 1;
-	return 0;
-}
-*/
-
-static int extract_text(char *dest, const unsigned char *src, int len, int swab)
-{
-	char *p = dest;
-	int i;
-
-	if (swab) swab = 1;
-
-	for (i = len - 1; i >= 0; i--)
-	{
-		if (src[i^swab] != ' ') break;
-	}
-	len = i + 1;
-
-	for (i = 0; i < len; i++)
-	{
-		unsigned char s = src[i^swab];
-		if (s >= 0x20 && s < 0x7f && s != '#' && s != '|' &&
-			s != '[' && s != ']' && s != '\\')
-		{
-			*p++ = s;
-		}
-		else
-		{
-			sprintf(p, "\\%02x", s);
-			p += 3;
-		}
-	}
-
-	return p - dest;
-}
-
-static char *emu_make_rom_id(const char *fname)
-{
-	static char id_string[3+0xe*3+0x3*3+0x30*3+3];
-	int pos, swab = 1;
-
-	if (PicoIn.AHW & PAHW_MCD) {
-		strcpy(id_string, "CD|");
-		swab = 0;
-	}
-	else if (PicoIn.AHW & PAHW_SMS)
-		strcpy(id_string, "MS|");
-	else	strcpy(id_string, "MD|");
-	pos = 3;
-
-	if (!(PicoIn.AHW & PAHW_SMS)) {
-		pos += extract_text(id_string + pos, media_id_header + 0x80, 0x0e, swab); // serial
-		id_string[pos] = '|'; pos++;
-		pos += extract_text(id_string + pos, media_id_header + 0xf0, 0x03, swab); // region
-		id_string[pos] = '|'; pos++;
-		pos += extract_text(id_string + pos, media_id_header + 0x50, 0x30, swab); // overseas name
-		id_string[pos] = 0;
-		if (pos > 5)
-			return id_string;
-		pos = 3;
-	}
-
-	// can't find name in ROM, use filename
-	fname_ext(id_string + 3, sizeof(id_string) - 3, NULL, NULL, fname);
-
-	return id_string;
-}
-
-// buffer must be at least 150 byte long
-void emu_get_game_name(char *str150)
-{
-	int ret, swab = (PicoIn.AHW & PAHW_MCD) ? 0 : 1;
-	char *s, *d;
-
-	ret = extract_text(str150, media_id_header + 0x50, 0x30, swab); // overseas name
-
-	for (s = d = str150 + 1; s < str150+ret; s++)
-	{
-		if (*s == 0) break;
-		if (*s != ' ' || d[-1] != ' ')
-			*d++ = *s;
-	}
-	*d = 0;
-}
-
-static void system_announce(void)
-{
-	const char *sys_name, *tv_standard, *extra = "";
-	int fps;
-
-	if (PicoIn.AHW & PAHW_SMS) {
-		sys_name = "Master System";
-#ifdef NO_SMS
-		extra = " [no support]";
-#endif
-	} else if (PicoIn.AHW & PAHW_PICO) {
-		sys_name = "Pico";
-	} else if ((PicoIn.AHW & (PAHW_32X|PAHW_MCD)) == (PAHW_32X|PAHW_MCD)) {
-		sys_name = "32X + Mega CD";
-		if ((Pico.m.hardware & 0xc0) == 0x80)
-			sys_name = "32X + Sega CD";
-	} else if (PicoIn.AHW & PAHW_MCD) {
-		sys_name = "Mega CD";
-		if ((Pico.m.hardware & 0xc0) == 0x80)
-			sys_name = "Sega CD";
-	} else if (PicoIn.AHW & PAHW_32X) {
-		sys_name = "32X";
-	} else {
-		sys_name = "MegaDrive";
-		if ((Pico.m.hardware & 0xc0) == 0x80)
-			sys_name = "Genesis";
-	}
-	tv_standard = Pico.m.pal ? "PAL" : "NTSC";
-	fps = Pico.m.pal ? 50 : 60;
-
-	//emu_status_msg("%s %s / %dFPS%s", tv_standard, sys_name, fps, extra);
-	printf("\nSystem Announce: %s, %s / %dFPS%s\n", sys_name, tv_standard, fps, extra);
-	printf("PicoIn.AHW = %d, Pico.m.hardware=%d\n", PicoIn.AHW, Pico.m.hardware);
-}
-
-static void do_region_override(const char *media_fname)
-{
-	// we only need to override region if config tells us so
-	int ret = emu_read_config(media_fname, 0);
-	if (!ret) emu_read_config(NULL, 0);
-}
-
-int emu_reload_rom(const char *rom_fname_in)
-{
-	// use setting before rom config is loaded
-	int autoload = g_autostateld_opt;
-	char *rom_fname = NULL;
-	char ext[5];
-	enum media_type_e media_type;
-	int menu_romload_started = 0;
-	char carthw_path[512];
-	int retval = 0;
-
-	lprintf("emu_ReloadRom(%s)\n", rom_fname_in);
-
-	rom_fname = strdup(rom_fname_in);
-	if (rom_fname == NULL)
-		return 0;
-
-	get_ext(rom_fname, ext);
-
-	// early cleanup
-	PicoPatchUnload();
-	if (movie_data) {
-		free(movie_data);
-		movie_data = 0;
-	}
-
-	if (!strcmp(ext, ".gmv"))
-	{
-		// check for both gmv and rom
-		int dummy;
-		FILE *movie_file = fopen(rom_fname, "rb");
-		if (!movie_file) {
-			menu_update_msg("Failed to open movie.");
-			goto out;
-		}
-		fseek(movie_file, 0, SEEK_END);
-		movie_size = ftell(movie_file);
-		fseek(movie_file, 0, SEEK_SET);
-		if (movie_size < 64+3) {
-			menu_update_msg("Invalid GMV file.");
-			fclose(movie_file);
-			goto out;
-		}
-		movie_data = malloc(movie_size);
-		if (movie_data == NULL) {
-			menu_update_msg("low memory.");
-			fclose(movie_file);
-			goto out;
-		}
-		dummy = fread(movie_data, 1, movie_size, movie_file);
-		fclose(movie_file);
-		if (strncmp((char *)movie_data, "Gens Movie TEST", 15) != 0) {
-			menu_update_msg("Invalid GMV file.");
-			goto out;
-		}
-		dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);
-		if (!dummy) {
-			menu_update_msg("Could't find a ROM for movie.");
-			goto out;
-		}
-		get_ext(rom_fname, ext);
-		lprintf("gmv loaded for %s\n", rom_fname);
-	}
-	else if (!strcmp(ext, ".pat"))
-	{
-		int dummy;
-		PicoPatchLoad(rom_fname);
-		dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);
-		if (!dummy) {
-			menu_update_msg("Could't find a ROM to patch.");
-			goto out;
-		}
-		get_ext(rom_fname, ext);
-	}
-
-	menu_romload_prepare(rom_fname); // also CD load
-	menu_romload_started = 1;
-
-	emu_make_path(carthw_path, "carthw.cfg", sizeof(carthw_path));
-
-	media_type = PicoLoadMedia(rom_fname, carthw_path,
-			find_bios, do_region_override);
-
-	switch (media_type) {
-	case PM_BAD_DETECT:
-		menu_update_msg("Not a ROM/CD img selected.");
-		goto out;
-	case PM_BAD_CD:
-		menu_update_msg("Invalid CD image");
-		goto out;
-	case PM_BAD_CD_NO_BIOS:
-		// find_bios() prints a message
-		goto out;
-	case PM_ERROR:
-		menu_update_msg("Load error");
-		goto out;
-	default:
-		break;
-	}
-
-	// make quirks visible in UI
-	if (PicoIn.quirks & PQUIRK_FORCE_6BTN)
-		currentConfig.input_dev0 = PICO_INPUT_PAD_6BTN;
-
-	menu_romload_end();
-	menu_romload_started = 0;
-
-	if (PicoPatches) {
-		PicoPatchPrepare();
-		PicoPatchApply();
-	}
-
-	// additional movie stuff
-	if (movie_data)
-	{
-		enum input_device indev = (movie_data[0x14] == '6') ?
-			PICO_INPUT_PAD_6BTN : PICO_INPUT_PAD_3BTN;
-		PicoSetInputDevice(0, indev);
-		PicoSetInputDevice(1, indev);
-
-		PicoIn.opt |= POPT_DIS_VDP_FIFO; // no VDP fifo timing
-		if (movie_data[0xF] >= 'A') {
-			if (movie_data[0x16] & 0x80) {
-				PicoIn.regionOverride = 8;
-			} else {
-				PicoIn.regionOverride = 4;
-			}
-			PicoReset();
-			// TODO: bits 6 & 5
-		}
-		movie_data[0x18+30] = 0;
-		emu_status_msg("MOVIE: %s", (char *) &movie_data[0x18]);
-	}
-	else
-	{
-		system_announce();
-		PicoIn.opt &= ~POPT_DIS_VDP_FIFO;
-	}
-
-	/* Set input map */
-	if (PicoIn.AHW & PAHW_SMS) {
-		printf("plat set sms input\n");
-		plat_set_sms_input();
-	}
-	else{
-		plat_set_genesis_input();
-	}
-
-	strncpy(rom_fname_loaded, rom_fname, sizeof(rom_fname_loaded)-1);
-	rom_fname_loaded[sizeof(rom_fname_loaded)-1] = 0;
-
-	// load SRAM for this ROM
-	if (currentConfig.EmuOpt & EOPT_EN_SRAM)
-		emu_save_load_game(1, 1);
-
-	// state autoload?
-	if (autoload) {
-		int time, newest = 0, newest_slot = -1;
-		int slot;
-
-		for (slot = 0; slot < 10; slot++) {
-			if (emu_check_save_file(slot, &time)) {
-				if (time > newest) {
-					newest = time;
-					newest_slot = slot;
-				}
-			}
-		}
-
-		if (newest_slot >= 0) {
-			lprintf("autoload slot %d\n", newest_slot);
-			state_slot = newest_slot;
-			emu_save_load_game(1, 0);
-		}
-		else {
-			lprintf("no save to autoload.\n");
-		}
-	}
-
-	retval = 1;
-out:
-	if (menu_romload_started)
-		menu_romload_end();
-	free(rom_fname);
-	return retval;
-}
-
-int emu_swap_cd(const char *fname)
-{
-	enum cd_track_type cd_type;
-	int ret = -1;
-
-	cd_type = PicoCdCheck(fname, NULL);
-	if (cd_type != CT_UNKNOWN)
-		ret = cdd_load(fname, cd_type);
-	if (ret != 0) {
-		menu_update_msg("Load failed, invalid CD image?");
-		return 0;
-	}
-
-	strncpy(rom_fname_loaded, fname, sizeof(rom_fname_loaded)-1);
-	rom_fname_loaded[sizeof(rom_fname_loaded) - 1] = 0;
-
-	return 1;
-}
-
-// <base dir><end>
-void emu_make_path(char *buff, const char *end, int size)
-{
-	int pos, end_len;
-
-	end_len = strlen(end);
-	pos = plat_get_root_dir(buff, size);
-	strncpy(buff + pos, end, size - pos);
-	buff[size - 1] = 0;
-	if (pos + end_len > size - 1)
-		lprintf("Warning: path truncated: %s\n", buff);
-}
-
-static void make_config_cfg(char *cfg_buff_512)
-{
-	emu_make_path(cfg_buff_512, PicoConfigFile, 512-6);
-	if (config_slot != 0)
-	{
-		char *p = strrchr(cfg_buff_512, '.');
-		if (p == NULL)
-			p = cfg_buff_512 + strlen(cfg_buff_512);
-		sprintf(p, ".%i.cfg", config_slot);
-	}
-	cfg_buff_512[511] = 0;
-}
-
-void emu_prep_defconfig(void)
-{
-	memset(&defaultConfig, 0, sizeof(defaultConfig));
-	defaultConfig.EmuOpt    = EOPT_EN_SRAM | EOPT_EN_SOUND | EOPT_16BPP |
-				  EOPT_EN_CD_LEDS | EOPT_GZIP_SAVES | 0x10/*?*/;
-	defaultConfig.s_PicoOpt = POPT_EN_SNDFILTER|
-				  POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80 |
-				  POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX |
-				  POPT_EN_DRC|POPT_ACC_SPRITES |
-				  POPT_EN_32X|POPT_EN_PWM;
-	defaultConfig.s_PsndRate = 44100;
-	defaultConfig.s_PicoRegion = 0; // auto
-	defaultConfig.s_PicoAutoRgnOrder = 0x184; // US, EU, JP
-	defaultConfig.s_PicoCDBuffers = 0;
-	defaultConfig.s_PicoSndFilterAlpha = 0x10000 * 60 / 100;
-	defaultConfig.confirm_save = EOPT_CONFIRM_SAVE;
-	defaultConfig.Frameskip = -1; // auto
-	defaultConfig.input_dev0 = PICO_INPUT_PAD_3BTN;
-	defaultConfig.input_dev1 = PICO_INPUT_PAD_3BTN;
-	defaultConfig.volume = 50;
-	//defaultConfig.volume = 99;
-	defaultConfig.gamma = 100;
-	defaultConfig.scaling = 0;
-	defaultConfig.turbo_rate = 15;
-	defaultConfig.msh2_khz = PICO_MSH2_HZ / 1000;
-	defaultConfig.ssh2_khz = PICO_SSH2_HZ / 1000;
-	defaultConfig.max_skip = 4;
-
-	// platform specific overrides
-	pemu_prep_defconfig();
-}
-
-void emu_set_defconfig(void)
-{
-	memcpy(&currentConfig, &defaultConfig, sizeof(currentConfig));
-	PicoIn.opt = currentConfig.s_PicoOpt;
-	PicoIn.sndRate = currentConfig.s_PsndRate;
-	PicoIn.regionOverride = currentConfig.s_PicoRegion;
-	PicoIn.autoRgnOrder = currentConfig.s_PicoAutoRgnOrder;
-	PicoIn.sndFilterAlpha = currentConfig.s_PicoSndFilterAlpha;
-}
-
-int emu_read_config(const char *rom_fname, int no_defaults)
-{
-	char cfg[512];
-	int ret;
-
-	if (!no_defaults)
-		emu_set_defconfig();
-
-	if (rom_fname == NULL)
-	{
-		// global config
-		make_config_cfg(cfg);
-		ret = config_readsect(cfg, NULL);
-	}
-	else
-	{
-		char ext[16];
-		int vol;
-
-		if (config_slot != 0)
-			snprintf(ext, sizeof(ext), ".%i.cfg", config_slot);
-		else
-			strcpy(ext, ".cfg");
-
-		fname_ext(cfg, sizeof(cfg), "cfg"PATH_SEP, ext, rom_fname); // gamepath/gamename.cfg
-
-		// read user's config
-		vol = currentConfig.volume;
-		ret = config_readsect(cfg, NULL);
-		currentConfig.volume = vol; // make vol global (bah)
-
-		if (ret != 0)
-		{
-			// read global config, and apply game_def.cfg on top
-			make_config_cfg(cfg); // $HOME/.picodrive/config2.cfg
-			config_readsect(cfg, NULL);
-
-			emu_make_path(cfg, "game_def.cfg", sizeof(cfg)); // $HOME/.picodrive/game_def.cfg
-			ret = config_readsect(cfg, emu_make_rom_id(rom_fname));
-		}
-	}
-
-	pemu_validate_config();
-	PicoIn.overclockM68k = currentConfig.overclock_68k;
-
-	// some sanity checks
-	if (currentConfig.volume < 0 || currentConfig.volume > 99)
-		currentConfig.volume = 50;
-
-	if (ret == 0)
-		config_slot_current = config_slot;
-
-	return (ret == 0);
-}
-
-
-int emu_write_config(int is_game)
-{
-	char cfg[512];
-	int ret, write_lrom = 0;
-
-	if (!is_game)
-	{
-		make_config_cfg(cfg);
-		write_lrom = 1;
-	} else {
-		char ext[16];
-
-		if (config_slot != 0)
-			snprintf(ext, sizeof(ext), ".%i.cfg", config_slot);
-		else
-			strcpy(ext, ".cfg");
-
-		romfname_ext(cfg, sizeof(cfg), "cfg"PATH_SEP, ext);
-	}
-
-	lprintf("emu_write_config: %s ", cfg);
-	ret = config_write(cfg);
-	if (write_lrom) config_writelrom(cfg);
-#ifdef __GP2X__
-	sync();
-#endif
-	lprintf((ret == 0) ? "(ok)\n" : "(failed)\n");
-
-	if (ret == 0) config_slot_current = config_slot;
-	return ret == 0;
-}
-
-
-/* always using built-in font */
-
-#define mk_text_out(name, type, val, topleft, step_x, step_y) \
-void name(int x, int y, const char *text)				\
-{									\
-	int i, l, len = strlen(text);					\
-	type *screen = (type *)(topleft) + x * step_x + y * step_y;	\
-									\
-	for (i = 0; i < len; i++, screen += 8 * step_x)			\
-	{								\
-		for (l = 0; l < 8; l++)					\
-		{							\
-			unsigned char fd = fontdata8x8[text[i] * 8 + l];\
-			type *s = screen + l * step_y;			\
-			if (fd&0x80) s[step_x * 0] = val;		\
-			if (fd&0x40) s[step_x * 1] = val;		\
-			if (fd&0x20) s[step_x * 2] = val;		\
-			if (fd&0x10) s[step_x * 3] = val;		\
-			if (fd&0x08) s[step_x * 4] = val;		\
-			if (fd&0x04) s[step_x * 5] = val;		\
-			if (fd&0x02) s[step_x * 6] = val;		\
-			if (fd&0x01) s[step_x * 7] = val;		\
-		}							\
-	}								\
-}
-
-mk_text_out(emu_text_out8,      unsigned char,    0xf0, g_screen_ptr, 1, g_screen_ppitch)
-mk_text_out(emu_text_out16,     unsigned short, 0xffff, g_screen_ptr, 1, g_screen_ppitch)
-mk_text_out(emu_text_out8_rot,  unsigned char,    0xf0,
-	(char *)g_screen_ptr  + (g_screen_ppitch - 1) * g_screen_height, -g_screen_height, 1)
-mk_text_out(emu_text_out16_rot, unsigned short, 0xffff,
-	(short *)g_screen_ptr + (g_screen_ppitch - 1) * g_screen_height, -g_screen_height, 1)
-
-#undef mk_text_out
-
-void emu_osd_text16(int x, int y, const char *text)
-{
-	int len = strlen(text) * 8;
-	int i, h;
-
-	len++;
-	if (x + len > g_screen_width)
-		len = g_screen_width - x;
-
-	for (h = 0; h < 8; h++) {
-		unsigned short *p;
-		p = (unsigned short *)g_screen_ptr
-			+ x + g_screen_ppitch * (y + h);
-		for (i = len; i > 0; i--, p++)
-			*p = (*p >> 2) & 0x39e7;
-	}
-	emu_text_out16(x, y, text);
-}
-
-static void update_movie(void)
-{
-	int offs = Pico.m.frame_count*3 + 0x40;
-	if (offs+3 > movie_size) {
-		free(movie_data);
-		movie_data = 0;
-		emu_status_msg("END OF MOVIE.");
-		lprintf("END OF MOVIE.\n");
-	} else {
-		// MXYZ SACB RLDU
-		PicoIn.pad[0] = ~movie_data[offs]   & 0x8f; // ! SCBA RLDU
-		if(!(movie_data[offs]   & 0x10)) PicoIn.pad[0] |= 0x40; // C
-		if(!(movie_data[offs]   & 0x20)) PicoIn.pad[0] |= 0x10; // A
-		if(!(movie_data[offs]   & 0x40)) PicoIn.pad[0] |= 0x20; // B
-		PicoIn.pad[1] = ~movie_data[offs+1] & 0x8f; // ! SCBA RLDU
-		if(!(movie_data[offs+1] & 0x10)) PicoIn.pad[1] |= 0x40; // C
-		if(!(movie_data[offs+1] & 0x20)) PicoIn.pad[1] |= 0x10; // A
-		if(!(movie_data[offs+1] & 0x40)) PicoIn.pad[1] |= 0x20; // B
-		PicoIn.pad[0] |= (~movie_data[offs+2] & 0x0A) << 8; // ! MZYX
-		if(!(movie_data[offs+2] & 0x01)) PicoIn.pad[0] |= 0x0400; // X
-		if(!(movie_data[offs+2] & 0x04)) PicoIn.pad[0] |= 0x0100; // Z
-		PicoIn.pad[1] |= (~movie_data[offs+2] & 0xA0) << 4; // ! MZYX
-		if(!(movie_data[offs+2] & 0x10)) PicoIn.pad[1] |= 0x0400; // X
-		if(!(movie_data[offs+2] & 0x40)) PicoIn.pad[1] |= 0x0100; // Z
-	}
-}
-
-static int try_ropen_file(const char *fname, int *time)
-{
-	struct stat st;
-	FILE *f;
-
-	f = fopen(fname, "rb");
-	if (f) {
-		if (time != NULL) {
-			*time = 0;
-			if (fstat(fileno(f), &st) == 0)
-				*time = (int)st.st_mtime;
-		}
-		fclose(f);
-		return 1;
-	}
-	return 0;
-}
-
-char *emu_get_save_fname(int load, int is_sram, int slot, int *time)
-{
-	char *saveFname = static_buff;
-	char ext[16];
-
-	if (is_sram)
-	{
-		strcpy(ext, (PicoIn.AHW & PAHW_MCD) ? ".brm" : ".srm");
-		romfname_ext(saveFname, sizeof(static_buff),
-			(PicoIn.AHW & PAHW_MCD) ? "brm"PATH_SEP : "srm"PATH_SEP, ext);
-		if (!load)
-			return saveFname;
-
-		if (try_ropen_file(saveFname, time))
-			return saveFname;
-
-		romfname_ext(saveFname, sizeof(static_buff), NULL, ext);
-		if (try_ropen_file(saveFname, time))
-			return saveFname;
-	}
-	else
-	{
-		const char *ext_main = (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds.gz" : ".mds";
-		const char *ext_othr = (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds" : ".mds.gz";
-		ext[0] = 0;
-		if (slot > 0 && slot < 10)
-			sprintf(ext, ".%i", slot);
-		strcat(ext, ext_main);
-
-		if (!load) {
-			romfname_ext(saveFname, sizeof(static_buff), "mds" PATH_SEP, ext);
-			return saveFname;
-		}
-		else {
-			romfname_ext(saveFname, sizeof(static_buff), "mds" PATH_SEP, ext);
-			if (try_ropen_file(saveFname, time))
-				return saveFname;
-
-			romfname_ext(saveFname, sizeof(static_buff), NULL, ext);
-			if (try_ropen_file(saveFname, time))
-				return saveFname;
-
-			// try the other ext
-			ext[0] = 0;
-			if (slot > 0 && slot < 10)
-				sprintf(ext, ".%i", slot);
-			strcat(ext, ext_othr);
-
-			romfname_ext(saveFname, sizeof(static_buff), "mds"PATH_SEP, ext);
-			if (try_ropen_file(saveFname, time))
-				return saveFname;
-		}
-	}
-
-	return NULL;
-}
-
-int emu_check_save_file(int slot, int *time)
-{
-	return emu_get_save_fname(1, 0, slot, time) ? 1 : 0;
-}
-
-int emu_save_load_game_from_file(int load, char *saveFname){
-	int ret = PicoState(saveFname, !load);
-	if (!ret) {
-		//emu_status_msg(load ? "STATE LOADED" : "STATE SAVED");
-	} else {
-		//emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");
-		ret = -1;
-	}
-
-	return ret;
-}
-
-
-int emu_save_load_game(int load, int sram)
-{
-	int ret = 0;
-	char *saveFname;
-
-	// make save filename
-	saveFname = emu_get_save_fname(load, sram, state_slot, NULL);
-	if (saveFname == NULL) {
-		/*if (!sram)
-			emu_status_msg(load ? "LOAD FAILED (missing file)" : "SAVE FAILED");*/
-		return -1;
-	}
-
-	lprintf("saveLoad (%i, %i): %s\n", load, sram, saveFname);
-
-	if (sram)
-	{
-		FILE *sramFile;
-		int sram_size;
-		unsigned char *sram_data;
-		int truncate = 1;
-		if (PicoIn.AHW & PAHW_MCD)
-		{
-			if (PicoIn.opt & POPT_EN_MCD_RAMCART) {
-				sram_size = 0x12000;
-				sram_data = Pico.sv.data;
-				if (sram_data)
-					memcpy(sram_data, Pico_mcd->bram, 0x2000);
-			} else {
-				sram_size = 0x2000;
-				sram_data = Pico_mcd->bram;
-				truncate  = 0; // the .brm may contain RAM cart data after normal brm
-			}
-		} else {
-			sram_size = Pico.sv.size;
-			sram_data = Pico.sv.data;
-		}
-		if (sram_data == NULL)
-			return 0; // cart saves forcefully disabled for this game
-
-		if (load)
-		{
-			sramFile = fopen(saveFname, "rb");
-			if (!sramFile)
-				return -1;
-			ret = fread(sram_data, 1, sram_size, sramFile);
-			ret = ret > 0 ? 0 : -1;
-			fclose(sramFile);
-			if ((PicoIn.AHW & PAHW_MCD) && (PicoIn.opt&POPT_EN_MCD_RAMCART))
-				memcpy(Pico_mcd->bram, sram_data, 0x2000);
-		} else {
-			// sram save needs some special processing
-			// see if we have anything to save
-			for (; sram_size > 0; sram_size--)
-				if (sram_data[sram_size-1]) break;
-
-			if (sram_size) {
-				sramFile = fopen(saveFname, truncate ? "wb" : "r+b");
-				if (!sramFile) sramFile = fopen(saveFname, "wb"); // retry
-				if (!sramFile) return -1;
-				ret = fwrite(sram_data, 1, sram_size, sramFile);
-				ret = (ret != sram_size) ? -1 : 0;
-				fclose(sramFile);
-#ifdef __GP2X__
-				sync();
-#endif
-			}
-		}
-		return ret;
-	}
-	else
-	{
-		ret = PicoState(saveFname, !load);
-		if (!ret) {
-#ifdef __GP2X__
-			if (!load) sync();
-#endif
-			//emu_status_msg(load ? "STATE LOADED" : "STATE SAVED");
-		} else {
-			//emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");
-			ret = -1;
-		}
-
-		return ret;
-	}
-}
-
-void emu_set_fastforward(int set_on)
-{
-	static void *set_PsndOut = NULL;
-	static int set_Frameskip, set_EmuOpt, is_on = 0;
-
-	if (set_on && !is_on) {
-		set_PsndOut = PicoIn.sndOut;
-		set_Frameskip = currentConfig.Frameskip;
-		set_EmuOpt = currentConfig.EmuOpt;
-		PicoIn.sndOut = NULL;
-		currentConfig.Frameskip = 8;
-		currentConfig.EmuOpt &= ~4;
-		currentConfig.EmuOpt |= EOPT_NO_FRMLIMIT;
-		is_on = 1;
-		emu_status_msg("FAST FORWARD");
-	}
-	else if (!set_on && is_on) {
-		PicoIn.sndOut = set_PsndOut;
-		currentConfig.Frameskip = set_Frameskip;
-		currentConfig.EmuOpt = set_EmuOpt;
-		PsndRerate(1);
-		is_on = 0;
-		// mainly to unbreak pcm
-		if (PicoIn.AHW & PAHW_MCD)
-			pcd_state_loaded();
-	}
-}
-
-static void emu_tray_open(void)
-{
-	engineState = PGS_TrayMenu;
-}
-
-static void emu_tray_close(void)
-{
-	emu_status_msg("CD tray closed.");
-}
-
-void emu_32x_startup(void)
-{
-	plat_video_toggle_renderer(0, 0); // HACK
-	system_announce();
-}
-
-void emu_reset_game(void)
-{
-	PicoReset();
-	reset_timing = 1;
-}
-
-void run_events_pico(unsigned int events)
-{
-	int lim_x;
-
-	if (events & PEV_PICO_SWINP) {
-		pico_inp_mode++;
-		if (pico_inp_mode > 2)
-			pico_inp_mode = 0;
-		switch (pico_inp_mode) {
-			case 2: emu_status_msg("Input: Pen on Pad"); break;
-			case 1: emu_status_msg("Input: Pen on Storyware"); break;
-			case 0: emu_status_msg("Input: Joystick");
-				PicoPicohw.pen_pos[0] = PicoPicohw.pen_pos[1] = 0x8000;
-				break;
-		}
-	}
-	if (events & PEV_PICO_PPREV) {
-		PicoPicohw.page--;
-		if (PicoPicohw.page < 0)
-			PicoPicohw.page = 0;
-		emu_status_msg("Page %i", PicoPicohw.page);
-	}
-	if (events & PEV_PICO_PNEXT) {
-		PicoPicohw.page++;
-		if (PicoPicohw.page > 6)
-			PicoPicohw.page = 6;
-		emu_status_msg("Page %i", PicoPicohw.page);
-	}
-
-	if (pico_inp_mode == 0)
-		return;
-
-	/* handle other input modes */
-	if (PicoIn.pad[0] & 1) pico_pen_y--;
-	if (PicoIn.pad[0] & 2) pico_pen_y++;
-	if (PicoIn.pad[0] & 4) pico_pen_x--;
-	if (PicoIn.pad[0] & 8) pico_pen_x++;
-	PicoIn.pad[0] &= ~0x0f; // release UDLR
-
-	lim_x = (Pico.video.reg[12]&1) ? 319 : 255;
-	if (pico_pen_y < 8)
-		pico_pen_y = 8;
-	if (pico_pen_y > 224 - PICO_PEN_ADJUST_Y)
-		pico_pen_y = 224 - PICO_PEN_ADJUST_Y;
-	if (pico_pen_x < 0)
-		pico_pen_x = 0;
-	if (pico_pen_x > lim_x - PICO_PEN_ADJUST_X)
-		pico_pen_x = lim_x - PICO_PEN_ADJUST_X;
-
-	PicoPicohw.pen_pos[0] = pico_pen_x;
-	if (!(Pico.video.reg[12] & 1))
-		PicoPicohw.pen_pos[0] += pico_pen_x / 4;
-	PicoPicohw.pen_pos[0] += 0x3c;
-	PicoPicohw.pen_pos[1] = pico_inp_mode == 1 ? (0x2f8 + pico_pen_y) : (0x1fc + pico_pen_y);
-}
-
-static void do_turbo(unsigned short *pad, int acts)
-{
-	static int turbo_pad = 0;
-	static unsigned char turbo_cnt[3] = { 0, 0, 0 };
-	int inc = currentConfig.turbo_rate * 2;
-
-	if (acts & 0x1000) {
-		turbo_cnt[0] += inc;
-		if (turbo_cnt[0] >= 60)
-			turbo_pad ^= 0x10, turbo_cnt[0] = 0;
-	}
-	if (acts & 0x2000) {
-		turbo_cnt[1] += inc;
-		if (turbo_cnt[1] >= 60)
-			turbo_pad ^= 0x20, turbo_cnt[1] = 0;
-	}
-	if (acts & 0x4000) {
-		turbo_cnt[2] += inc;
-		if (turbo_cnt[2] >= 60)
-			turbo_pad ^= 0x40, turbo_cnt[2] = 0;
-	}
-	*pad |= turbo_pad & (acts >> 8);
-}
-
-static void run_events_ui(unsigned int which)
-{
-	char shell_cmd[100];
-	FILE *fp;
-	//emu_action_old = emu_action;
-	//printf("New event: %d\n", which);
-
-	if (which & (PEV_STATE_LOAD|PEV_STATE_SAVE))
-	{
-		int do_it = 1;
-		if ( emu_check_save_file(state_slot, NULL) &&
-			(((which & PEV_STATE_LOAD) && (currentConfig.confirm_save & EOPT_CONFIRM_LOAD)) ||
-			 ((which & PEV_STATE_SAVE) && (currentConfig.confirm_save & EOPT_CONFIRM_SAVE))) )
-		{
-			const char *nm;
-			char tmp[64];
-			int keys, len;
-
-			strcpy(tmp, (which & PEV_STATE_LOAD) ? "LOAD STATE? " : "OVERWRITE SAVE? ");
-			len = strlen(tmp);
-			nm = in_get_key_name(-1, -PBTN_MOK);
-			snprintf(tmp + len, sizeof(tmp) - len, "(%s=yes, ", nm);
-			len = strlen(tmp);
-			nm = in_get_key_name(-1, -PBTN_MBACK);
-			snprintf(tmp + len, sizeof(tmp) - len, "%s=no)", nm);
-
-			plat_status_msg_busy_first(tmp);
-
-			in_set_config_int(0, IN_CFG_BLOCKING, 1);
-			while (in_menu_wait_any(NULL, 50) & (PBTN_MOK | PBTN_MBACK))
-				;
-			while ( !((keys = in_menu_wait_any(NULL, 50)) & (PBTN_MOK | PBTN_MBACK)))
-				;
-			if (keys & PBTN_MBACK)
-				do_it = 0;
-			while (in_menu_wait_any(NULL, 50) & (PBTN_MOK | PBTN_MBACK))
-				;
-			in_set_config_int(0, IN_CFG_BLOCKING, 0);
-			plat_status_msg_clear();
-		}
-		if (do_it) {
-			plat_status_msg_busy_first((which & PEV_STATE_LOAD) ? "LOADING STATE" : "SAVING STATE");
-			PicoStateProgressCB = plat_status_msg_busy_next;
-			emu_save_load_game((which & PEV_STATE_LOAD) ? 1 : 0, 0);
-			PicoStateProgressCB = NULL;
-		}
-	}
-	if (which & PEV_SWITCH_RND)
-	{
-		plat_video_toggle_renderer(1, 0);
-	}
-	if (which & PEV_VOL_DOWN)
-	{
-		printf("PEV_VOL_DOWN\r\n");
-		/// ----- Compute new value -----
-		volume_percentage = (volume_percentage < STEP_CHANGE_VOLUME)?
-			0:(volume_percentage-STEP_CHANGE_VOLUME);
-		/// ----- HUD msg ------
-		char txt[100];
-		sprintf(txt, "VOLUME %d%%", volume_percentage);
-		plat_status_msg_busy_first(txt);
-		/// ----- Shell cmd ----
-		sprintf(shell_cmd, "%s %d", SHELL_CMD_VOLUME_SET, volume_percentage);
-		fp = popen(shell_cmd, "r");
-		if (fp == NULL) {
-			printf("Failed to run command %s\n", shell_cmd);
-		} else {
-	        	pclose(fp);
-		}
-	}
-	if (which & PEV_VOL_UP)
-	{
-		printf("PEV_VOL_UP\r\n");
-		/// ----- Compute new value -----
-		volume_percentage = (volume_percentage > 100 - STEP_CHANGE_VOLUME)?
-			100:(volume_percentage+STEP_CHANGE_VOLUME);
-		/// ----- HUD msg ------
-		char txt[100];
-		sprintf(txt, "VOLUME %d%%", volume_percentage);
-		plat_status_msg_busy_first(txt);
-		/// ----- Shell cmd ----
-		sprintf(shell_cmd, "%s %d", SHELL_CMD_VOLUME_SET, volume_percentage);
-		fp = popen(shell_cmd, "r");
-		if (fp == NULL) {
-			printf("Failed to run command %s\n", shell_cmd);
-		} else {
-	        	pclose(fp);
-		}
-	}
-	if (which & PEV_BRIGHT_UP)
-	{
-		printf("PEV_BRIGHT_UP\r\n");
-		/// ----- Compute new value -----
-		brightness_percentage = (brightness_percentage > 100 - STEP_CHANGE_BRIGHTNESS)?
-			100:(brightness_percentage+STEP_CHANGE_BRIGHTNESS);
-		/// ----- HUD msg ------
-		char txt[100];
-		sprintf(txt, "BRIGHTNESS %d%%", brightness_percentage);
-		plat_status_msg_busy_first(txt);
-		/// ----- Shell cmd ----
-		sprintf(shell_cmd, "%s %d", SHELL_CMD_BRIGHTNESS_SET, brightness_percentage);
-		fp = popen(shell_cmd, "r");
-		if (fp == NULL) {
-			printf("Failed to run command %s\n", shell_cmd);
-		} else {
-	        	pclose(fp);
-		}
-	}
-	if (which & PEV_BRIGHT_DOWN)
-	{
-		printf("PEV_BRIGHT_DOWN\r\n");
-		/// ----- Compute new value -----
-		brightness_percentage = (brightness_percentage < STEP_CHANGE_BRIGHTNESS)?
-			0:(brightness_percentage-STEP_CHANGE_BRIGHTNESS);
-		/// ----- HUD msg ------
-		char txt[100];
-		sprintf(txt, "BRIGHTNESS %d%%", brightness_percentage);
-		plat_status_msg_busy_first(txt);
-		/// ----- Shell cmd ----
-		sprintf(shell_cmd, "%s %d", SHELL_CMD_BRIGHTNESS_SET, brightness_percentage);
-		fp = popen(shell_cmd, "r");
-		if (fp == NULL) {
-			printf("Failed to run command %s\n", shell_cmd);
-		} else {
-	        	pclose(fp);
-		}
-	}
-	if (which & PEV_AR_FACT_UP)
-	{
-		printf("PEV_AR_FACT_UP\r\n");
-		/// ----- Compute new value -----
-		if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){
-			aspect_ratio_factor_percent = (aspect_ratio_factor_percent+aspect_ratio_factor_step<100)?
-				aspect_ratio_factor_percent+aspect_ratio_factor_step:100;
-			need_screen_cleared = 1;
-		}
-		else{
-			aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
-		}
-		aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
-		/// ----- HUD msg ------
-		/*char txt[100];
-		sprintf(txt, "    DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent);
-		plat_status_msg_busy_first(txt);*/
-		sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: ZOOMED %d%%%%\"",
-			SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent);
-		fp = popen(shell_cmd, "r");
-		if (fp == NULL) {
-			printf("Failed to run command %s\n", shell_cmd);
-		} else {
-	        	pclose(fp);
-		}
-
-        // Save config file
-        configfile_save(cfg_file_rom);
-	}
-	if (which & PEV_AR_FACT_DOWN)
-	{
-		printf("PEV_AR_FACT_DOWN\r\n");
-		/// ----- Compute new value -----
-		if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){
-			aspect_ratio_factor_percent = (aspect_ratio_factor_percent>aspect_ratio_factor_step)?
-				aspect_ratio_factor_percent-aspect_ratio_factor_step:0;
-			need_screen_cleared = 1;
-		}
-		else{
-			aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
-		}
-		aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
-		/// ----- HUD msg ------
-		/*char txt[100];
-		sprintf(txt, "    DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent);
-		plat_status_msg_busy_first(txt);*/
-		sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: ZOOMED %d%%%%\"",
-			SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent);
-		fp = popen(shell_cmd, "r");
-		if (fp == NULL) {
-			printf("Failed to run command %s\n", shell_cmd);
-		} else {
-	        	pclose(fp);
-		}
-
-        // Save config file
-        configfile_save(cfg_file_rom);
-
-	}
-	if (which & PEV_DISPMODE)
-	{
-		printf("PEV_DISPMODE\r\n");
-		/// ----- Compute new value -----
-		aspect_ratio = (aspect_ratio+1)%NB_ASPECT_RATIOS_TYPES;
-		/// ----- HUD msg ------
-		//char txt[100];
-		if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){
-			//sprintf(txt, "    DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent);
-			sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: ZOOMED %d%%%%\"",
-				SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent);
-		}
-		else{
-			//sprintf(txt, "DISPLAY MODE: %s", aspect_ratio_name[aspect_ratio]);
-			sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: %s\"",
-				SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_name[aspect_ratio]);
-		}
-		//plat_status_msg_busy_first(txt);
-		fp = popen(shell_cmd, "r");
-		if (fp == NULL) {
-			printf("Failed to run command %s\n", shell_cmd);
-		} else {
-	        	pclose(fp);
-		}
-
-        // Save config file
-        configfile_save(cfg_file_rom);
-	}
-	if (which & (PEV_SSLOT_PREV|PEV_SSLOT_NEXT))
-	{
-		if (which & PEV_SSLOT_PREV) {
-			state_slot -= 1;
-			if (state_slot < 0)
-				state_slot = 9;
-		} else {
-			state_slot += 1;
-			if (state_slot > 9)
-				state_slot = 0;
-		}
-
-		emu_status_msg("SAVE SLOT %i [%s]", state_slot,
-			emu_check_save_file(state_slot, NULL) ? "USED" : "FREE");
-	}
-	if (which & PEV_RESET)
-		emu_reset_game();
-	if (which & PEV_MENU)
-		engineState = PGS_Menu;
-}
-
-void emu_update_input(void)
-{
-	static int prev_events = 0;
-	int actions[IN_BINDTYPE_COUNT] = { 0, };
-	int pl_actions[2];
-	int events;
-
-	in_update(actions);
-
-	pl_actions[0] = actions[IN_BINDTYPE_PLAYER12];
-	pl_actions[1] = actions[IN_BINDTYPE_PLAYER12] >> 16;
-
-	PicoIn.pad[0] = pl_actions[0] & 0xfff;
-	PicoIn.pad[1] = pl_actions[1] & 0xfff;
-
-	if (pl_actions[0] & 0x7000)
-		do_turbo(&PicoIn.pad[0], pl_actions[0]);
-	if (pl_actions[1] & 0x7000)
-		do_turbo(&PicoIn.pad[1], pl_actions[1]);
-
-	events = actions[IN_BINDTYPE_EMU] & PEV_MASK;
-
-	// volume is treated in special way and triggered every frame
-	if (events & (PEV_VOL_DOWN|PEV_VOL_UP))
-		plat_update_volume(1, events & PEV_VOL_UP);
-
-	if ((events ^ prev_events) & PEV_FF) {
-		emu_set_fastforward(events & PEV_FF);
-		plat_update_volume(0, 0);
-		reset_timing = 1;
-	}
-
-	events &= ~prev_events;
-
-	/* SMS */
-	if (PicoIn.AHW == PAHW_PICO)
-		run_events_pico(events);
-
-	if (events)
-		run_events_ui(events);
-
-	if (movie_data)
-		update_movie();
-
-	prev_events = actions[IN_BINDTYPE_EMU] & PEV_MASK;
-}
-
-static void mkdir_path(char *path_with_reserve, int pos, const char *name)
-{
-	strcpy(path_with_reserve + pos, name);
-	if (plat_is_dir(path_with_reserve))
-		return;
-	if (mkdir(path_with_reserve, 0777) < 0)
-		lprintf("failed to create: %s\n", path_with_reserve);
-}
-
-void emu_cmn_forced_frame(int no_scale, int do_emu, void *buf)
-{
-	int po_old = PicoIn.opt;
-	int y;
-
-	for (y = 0; y < g_screen_height; y++)
-		memset32((short *)g_screen_ptr + g_screen_ppitch * y, 0,
-			 g_screen_width * 2 / 4);
-
-	PicoIn.opt &= ~POPT_ALT_RENDERER;
-	PicoIn.opt |= POPT_ACC_SPRITES;
-	if (!no_scale && currentConfig.scaling)
-		PicoIn.opt |= POPT_EN_SOFTSCALE;
-
-	PicoDrawSetOutFormat(PDF_RGB555, 1);
-	PicoDrawSetOutBuf(buf, g_screen_ppitch * 2);
-	Pico.m.dirtyPal = 1;
-	Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;
-	if (do_emu)
-		PicoFrame();
-	else
-		PicoFrameDrawOnly();
-
-	PicoIn.opt = po_old;
-}
-
-int emu_is_segaCD(){
-	return (PicoIn.AHW & PAHW_MCD);
-}
-
-/* Quick save and turn off the console */
-void quick_save_and_poweroff()
-{
-    FILE *fp;
-
-    printf("Save Instant Play file\n");
-
-    /* Send command to cancel any previously scheduled powerdown */
-    fp = popen(SHELL_CMD_POWERDOWN_HANDLE, "r");
-    if (fp == NULL)
-    {
-        /* Countdown is still ticking, so better do nothing
-	   than start writing and get interrupted!
-	*/
-        printf("Failed to cancel scheduled shutdown\n");
-	exit(0);
-    }
-    pclose(fp);
-
-    /* Save  */
-    emu_save_load_game_from_file(0, quick_save_file);
-
-    /* Perform Instant Play save and shutdown */
-    execlp(SHELL_CMD_INSTANT_PLAY, SHELL_CMD_INSTANT_PLAY,
-	"save", prog_name, "-loadStateFile", quick_save_file, mRomName, NULL);
-
-    /* Should not be reached */
-    printf("Failed to perform Instant Play save and shutdown\n");
-
-    /* Exit Emulator */
-    exit(0);
-}
-
-
-void emu_init(void)
-{
-	char path[512];
-	int pos;
-
-#if 0
-	// FIXME: handle through menu, etc
-	FILE *f;
-	f = fopen("32X_M_BIOS.BIN", "rb");
-	p32x_bios_m = malloc(2048);
-	fread(p32x_bios_m, 1, 2048, f);
-	fclose(f);
-	f = fopen("32X_S_BIOS.BIN", "rb");
-	p32x_bios_s = malloc(1024);
-	fread(p32x_bios_s, 1, 1024, f);
-	fclose(f);
-#endif
-
-	/* make dirs for saves */
-	pos = plat_get_root_dir(path, sizeof(path) - 4);
-	mkdir_path(path, pos, "mds");
-	mkdir_path(path, pos, "srm");
-	mkdir_path(path, pos, "brm");
-	mkdir_path(path, pos, "cfg");
-
-	pprof_init();
-
-	make_config_cfg(path);
-	config_readlrom(path);
-
-	PicoInit();
-	PicoIn.osdMessage = plat_status_msg_busy_next;
-	PicoIn.mcdTrayOpen = emu_tray_open;
-	PicoIn.mcdTrayClose = emu_tray_close;
-
-	sndout_init();
-}
-
-void emu_finish(void)
-{
-	// save SRAM
-	if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && Pico.sv.changed) {
-		emu_save_load_game(0, 1);
-		Pico.sv.changed = 0;
-	}
-
-	if (!(currentConfig.EmuOpt & EOPT_NO_AUTOSVCFG)) {
-		char cfg[512];
-		make_config_cfg(cfg);
-		config_writelrom(cfg);
-#ifdef __GP2X__
-		sync();
-#endif
-	}
-
-	pprof_finish();
-
-	PicoExit();
-	sndout_exit();
-}
-
-static void snd_write_nonblocking(int len)
-{
-	sndout_write_nb(PicoIn.sndOut, len);
-}
-
-void emu_sound_start(void)
-{
-	PicoIn.sndOut = NULL;
-
-	if (currentConfig.EmuOpt & EOPT_EN_SOUND)
-	{
-		int is_stereo = (PicoIn.opt & POPT_EN_STEREO) ? 1 : 0;
-		/// Hard Bypass Stereo to mono
-		//is_stereo = 0;
-
-		PsndRerate(Pico.m.frame_count ? 1 : 0);
-
-		printf("starting audio: %i len: %i stereo: %i, pal: %i\n",
-			PicoIn.sndRate, Pico.snd.len, is_stereo, Pico.m.pal);
-		sndout_start(PicoIn.sndRate, is_stereo);
-		PicoIn.writeSound = snd_write_nonblocking;
-		plat_update_volume(0, 0);
-		memset(sndBuffer, 0, sizeof(sndBuffer));
-		PicoIn.sndOut = sndBuffer;
-	}
-}
-
-void emu_sound_stop(void)
-{
-	sndout_stop();
-}
-
-void emu_sound_wait(void)
-{
-	sndout_wait();
-}
-
-static void emu_loop_prep(void)
-{
-	static int pal_old = -1;
-	static int filter_old = -1;
-
-	if (currentConfig.CPUclock != plat_target_cpu_clock_get())
-		plat_target_cpu_clock_set(currentConfig.CPUclock);
-
-	if (Pico.m.pal != pal_old) {
-		plat_target_lcdrate_set(Pico.m.pal);
-		pal_old = Pico.m.pal;
-	}
-
-	if (currentConfig.filter != filter_old) {
-		plat_target_hwfilter_set(currentConfig.filter);
-		filter_old = currentConfig.filter;
-	}
-
-	plat_target_gamma_set(currentConfig.gamma, 0);
-
-	pemu_loop_prep();
-}
-
-/* our tick here is 1 us right now */
-#define ms_to_ticks(x) (unsigned int)(x * 1000)
-#define get_ticks() plat_get_ticks_us()
-
-void emu_loop(void)
-{
-	int frames_done, frames_shown;	/* actual frames for fps counter */
-	int frame_nb = 0;	
-	int target_frametime_x3;
-	unsigned int timestamp_x3 = 0;
-	unsigned int timestamp_aim_x3 = 0;
-	unsigned int timestamp_fps_x3 = 0;
-	char *notice_msg = NULL;
-	char fpsbuff[24];
-	int fskip_cnt = 0;
-
-	fpsbuff[0] = 0;
-
-	PicoLoopPrepare();
-
-	plat_video_loop_prepare();
-	emu_loop_prep();
-	pemu_sound_start();
-
-	/* number of ticks per frame */
-	if (Pico.m.pal)
-		target_frametime_x3 = 3 * ms_to_ticks(1000) / 50;
-	else
-		target_frametime_x3 = 3 * ms_to_ticks(1000) / 60;
-
-	reset_timing = 1;
-	frames_done = frames_shown = 0;
-
-	/* loop with resync every 1 sec. */
-	while (engineState == PGS_Running)
-	{
-		int skip = 0;
-		int diff;
-
-		pprof_start(main);
-
-		if (reset_timing) {
-			reset_timing = 0;
-			plat_video_wait_vsync();
-			timestamp_aim_x3 = get_ticks() * 3;
-			timestamp_fps_x3 = timestamp_aim_x3;
-			fskip_cnt = 0;
-		}
-		else if (currentConfig.EmuOpt & EOPT_NO_FRMLIMIT) {
-			timestamp_aim_x3 = get_ticks() * 3;
-		}
-
-		timestamp_x3 = get_ticks() * 3;
-
-		// show notice_msg message?
-		if (notice_msg_time != 0)
-		{
-			static int noticeMsgSum;
-			if (timestamp_x3 - ms_to_ticks(notice_msg_time) * 3
-			     > ms_to_ticks(STATUS_MSG_TIMEOUT) * 3)
-			{
-				notice_msg_time = 0;
-				notice_msg = NULL;
-				plat_status_msg_clear();
-			}
-			else {
-				int sum = noticeMsg[0] + noticeMsg[1] + noticeMsg[2];
-				if (sum != noticeMsgSum) {
-					plat_status_msg_clear();
-					noticeMsgSum = sum;
-				}
-				notice_msg = noticeMsg;
-			}
-		}
-
-		// second changed?
-		if (timestamp_x3 - timestamp_fps_x3 >= ms_to_ticks(1000) * 3)
-		{
-#define FUNKEY_RESYNCHRONIZE_AUDIO_SECS	(10*60)
-#ifdef FUNKEY_RESYNCHRONIZE_AUDIO_SECS
-			static unsigned int last_resync = 0, cur_sec=0;
-			if(cur_sec++ - last_resync >= FUNKEY_RESYNCHRONIZE_AUDIO_SECS){
-				last_resync = cur_sec;
-
-				/** Resync here (same as quitting ans relaunching loop for now) */
-				printf("Resync sound now\n");
-				emu_sound_stop();
-				pemu_sound_start();
-			}
-#endif //FUNKEY_RESYNCHRONIZE_AUDIO_SECS
-
-#ifdef BENCHMARK
-			static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];
-			if (++bench == 10) {
-				bench = 0;
-				bench_fps_s = bench_fps;
-				bf[bfp++ & 3] = bench_fps;
-				bench_fps = 0;
-			}
-			bench_fps += frames_shown;
-			sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);
-			printf("%s\n", fpsbuff);
-#else
-			if (currentConfig.EmuOpt & EOPT_SHOW_FPS || show_fps_bypass){
-				printf("%02i/%02i  \n", frames_shown, frames_done);
-				snprintf(fpsbuff, 8, "%02i/%02i  ", frames_shown, frames_done);
-			}
-#endif
-			frames_shown = frames_done = 0;
-			timestamp_fps_x3 += ms_to_ticks(1000) * 3;
-		}
-#ifdef PFRAMES
-		sprintf(fpsbuff, "%i", Pico.m.frame_count);
-#endif
-
-		diff = timestamp_aim_x3 - timestamp_x3;
-
-		if (currentConfig.Frameskip >= 0) // frameskip enabled (or 0)
-		{
-			if (fskip_cnt < currentConfig.Frameskip) {
-				fskip_cnt++;
-				skip = 1;
-			}
-			else {
-				fskip_cnt = 0;
-			}
-		}
-		else if (diff < -target_frametime_x3)
-		{
-			/* no time left for this frame - skip */
-			/* limit auto frameskip to max_skip */
-			if (fskip_cnt < currentConfig.max_skip) {
-				fskip_cnt++;
-				skip = 1;
-			}
-			else {
-				fskip_cnt = 0;
-			}
-		} else
-			fskip_cnt = 0;
-
-		// don't go in debt too much
-		while (diff < -target_frametime_x3 * 3) {
-			timestamp_aim_x3 += target_frametime_x3;
-			diff = timestamp_aim_x3 - timestamp_x3;
-		}
-
-        /* Quick save and poweroff */
-        if(mQuickSaveAndPoweroff){
-			quick_save_and_poweroff();
-			mQuickSaveAndPoweroff = 0;
-        }
-
-		emu_update_input();
-		if (skip) {
-			int do_audio = diff > -target_frametime_x3 * 2;
-			PicoIn.skipFrame = do_audio ? 1 : 2;
-			PicoFrame();
-			PicoIn.skipFrame = 0;
-		}
-		else {
-			PicoFrame();
-			pemu_finalize_frame(fpsbuff, notice_msg);
-			frames_shown++;
-		}
-		frames_done++;
-		timestamp_aim_x3 += target_frametime_x3;
-
-		if (!skip && !flip_after_sync)
-			plat_video_flip();
-
-		/* frame limiter */
-		if (!skip && !reset_timing
-		    && !(currentConfig.EmuOpt & (EOPT_NO_FRMLIMIT|EOPT_EXT_FRMLIMIT)))
-		{
-			unsigned int timestamp = get_ticks();
-			diff = timestamp_aim_x3 - timestamp * 3;
-
-			// sleep or vsync if we are still too fast
-			if (diff > target_frametime_x3 && (currentConfig.EmuOpt & EOPT_VSYNC)) {
-				// we are too fast
-				plat_video_wait_vsync();
-				timestamp = get_ticks();
-				diff = timestamp * 3 - timestamp_aim_x3;
-			}
-			if (diff > target_frametime_x3) {
-				// still too fast
-				plat_wait_till_us(timestamp + (diff - target_frametime_x3) / 3);
-			}
-		}
-
-		if (!skip && flip_after_sync)
-			plat_video_flip();
-
-
-		/* FOR SEGA CD */
-		/* Leave some time for the bios to load before starting a quick load at boot */
-		/* (Sega CD doesn't quick load at boot otherwise) */
-		/* Should have a much cleaner implementation thant this fix */
-		if((PicoIn.AHW & PAHW_MCD) && frame_nb >= 120){
-
-			/* Load slot */
-			if(load_state_slot != -1){
-				printf("LOADING FROM SLOT (SEGA CD) %d...\n", load_state_slot+1);
-				char fname[1024];
-				emu_save_load_game(1, 0);
-				printf("LOADED FROM SLOT (SEGA CD) %d\n", load_state_slot+1);
-				load_state_slot = -1;
-			}
-			if(need_quick_load != -1){
-				load_state_file = quick_save_file;
-				need_quick_load = -1;
-			}
-			/* Load file */
-			if(load_state_file != NULL){
-				printf("LOADING FROM FILE (SEGA CD) %s...\n", load_state_file);
-				emu_save_load_game_from_file(1, load_state_file);
-				printf("LOADED FROM SLOT (SEGA CD) %s\n", load_state_file);
-				load_state_file = NULL;
-			}
-		}
-
-		frame_nb++;
-
-		pprof_end(main);
-	}
-
-	emu_set_fastforward(0);
-
-	// save SRAM
-	if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && Pico.sv.changed) {
-		plat_status_msg_busy_first("Writing SRAM/BRAM...");
-		emu_save_load_game(0, 1);
-		Pico.sv.changed = 0;
-	}
-
-	pemu_loop_end();
-	emu_sound_stop();
-}
+/*
+ * PicoDrive
+ * (C) notaz, 2007-2010
+ *
+ * This work is licensed under the terms of MAME license.
+ * See COPYING file in the top-level directory.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#ifdef __GP2X__
+#include <unistd.h>
+#endif
+
+#include "../libpicofe/posix.h"
+#include "../libpicofe/input.h"
+#include "../libpicofe/fonts.h"
+#include "../libpicofe/sndout.h"
+#include "../libpicofe/lprintf.h"
+#include "../libpicofe/plat.h"
+#include "emu.h"
+#include "configfile_fk.h"
+#include "input_pico.h"
+#include "menu_pico.h"
+#include "config_file.h"
+
+#include <pico/pico_int.h>
+#include <pico/patch.h>
+
+#ifdef USE_LIBRETRO_VFS
+#include "file_stream_transforms.h"
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 7
+#pragma GCC diagnostic ignored "-Wformat-truncation"
+#endif
+
+#ifndef _WIN32
+#define PATH_SEP      "/"
+#define PATH_SEP_C    '/'
+#else
+#define PATH_SEP      "\\"
+#define PATH_SEP_C    '\\'
+#endif
+
+#define STATUS_MSG_TIMEOUT 2000
+
+void *g_screen_ptr;
+
+int g_screen_width  = 320;
+int g_screen_height = 240;
+int g_screen_ppitch = 320; // pitch in pixels
+
+const char *PicoConfigFile = "config2.cfg";
+currentConfig_t currentConfig, defaultConfig;
+int state_slot = 0;
+int config_slot = 0, config_slot_current = 0;
+int pico_pen_x = 320/2, pico_pen_y = 240/2;
+int pico_inp_mode;
+int flip_after_sync;
+int engineState = PGS_Menu;
+int show_fps_bypass = 0;
+int need_screen_cleared = 0;
+
+static short __attribute__((aligned(4))) sndBuffer[2*44100/50];
+
+/* tmp buff to reduce stack usage for plats with small stack */
+static char static_buff[1024];
+const char *rom_fname_reload;
+char rom_fname_loaded[1024];
+int reset_timing = 0;
+static unsigned int notice_msg_time;	/* when started showing */
+static char noticeMsg[40];
+
+unsigned char *movie_data = NULL;
+static int movie_size = 0;
+
+
+/* don't use tolower() for easy old glibc binary compatibility */
+static void strlwr_(char *string)
+{
+	char *p;
+	for (p = string; *p; p++)
+		if ('A' <= *p && *p <= 'Z')
+			*p += 'a' - 'A';
+}
+
+static int try_rfn_cut(char *fname)
+{
+	FILE *tmp;
+	char *p;
+
+	p = fname + strlen(fname) - 1;
+	for (; p > fname; p--)
+		if (*p == '.') break;
+	*p = 0;
+
+	if((tmp = fopen(fname, "rb"))) {
+		fclose(tmp);
+		return 1;
+	}
+	return 0;
+}
+
+static void get_ext(const char *file, char *ext)
+{
+	const char *p;
+
+	p = file + strlen(file) - 4;
+	if (p < file) p = file;
+	strncpy(ext, p, 4);
+	ext[4] = 0;
+	strlwr_(ext);
+}
+
+static void fname_ext(char *dst, int dstlen, const char *prefix, const char *ext, const char *fname)
+{
+	int prefix_len = 0;
+	const char *p;
+
+	*dst = 0;
+	if (prefix) {
+		/*int len = plat_get_root_dir(dst, dstlen);
+		strcpy(dst + len, prefix);
+		prefix_len = len + strlen(prefix);*/
+
+		/* Saves are in ROM folder */
+		prefix_len = strlen(mRomPath)+1;
+		sprintf(dst, "%s/", mRomPath);
+	}
+
+	p = fname + strlen(fname) - 1;
+	for (; p >= fname && *p != PATH_SEP_C; p--)
+		;
+	p++;
+	strncpy(dst + prefix_len, p, dstlen - prefix_len - 1);
+
+	dst[dstlen - 8] = 0;
+	if ((p = strrchr(dst, '.')) != NULL)
+		dst[p-dst] = 0;
+	if (ext)
+		strcat(dst, ext);
+}
+
+static void romfname_ext(char *dst, int dstlen, const char *prefix, const char *ext)
+{
+	fname_ext(dst, dstlen, prefix, ext, rom_fname_loaded);
+}
+
+void emu_status_msg(const char *format, ...)
+{
+	va_list vl;
+	int ret;
+
+	va_start(vl, format);
+	ret = vsnprintf(noticeMsg, sizeof(noticeMsg), format, vl);
+	va_end(vl);
+
+	/* be sure old text gets overwritten */
+	for (; ret < 28; ret++)
+		noticeMsg[ret] = ' ';
+	noticeMsg[ret] = 0;
+
+	notice_msg_time = plat_get_ticks_ms();
+}
+
+static const char * const biosfiles_us[] = {
+	"us_scd2_9306", "SegaCDBIOS9303", "us_scd1_9210", "bios_CD_U"
+};
+static const char * const biosfiles_eu[] = {
+	"eu_mcd2_9306", "eu_mcd2_9303", "eu_mcd1_9210", "bios_CD_E"
+};
+static const char * const biosfiles_jp[] = {
+	"jp_mcd2_921222", "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J"
+};
+
+static const char *find_bios(int *region, const char *cd_fname)
+{
+	int i, count;
+	const char * const *files;
+	FILE *f = NULL;
+	int ret;
+
+	// we need to have config loaded at this point
+	ret = emu_read_config(cd_fname, 0);
+	if (!ret) emu_read_config(NULL, 0);
+
+	if (PicoIn.regionOverride) {
+		*region = PicoIn.regionOverride;
+		lprintf("override region to %s\n", *region != 4 ?
+			(*region == 8 ? "EU" : "JAP") : "USA");
+	}
+
+	if (*region == 4) { // US
+		files = biosfiles_us;
+		count = sizeof(biosfiles_us) / sizeof(char *);
+	} else if (*region == 8) { // EU
+		files = biosfiles_eu;
+		count = sizeof(biosfiles_eu) / sizeof(char *);
+	} else if (*region == 1 || *region == 2) {
+		files = biosfiles_jp;
+		count = sizeof(biosfiles_jp) / sizeof(char *);
+	} else {
+		return 0;
+	}
+
+	/** Check Bios files in home directory */
+	for (i = 0; i < count; i++)
+	{
+		emu_make_path(static_buff, files[i], sizeof(static_buff) - 4);
+		//printf("bios name static_buff: %s\n", static_buff);
+		strcat(static_buff, ".bin");
+		f = fopen(static_buff, "rb");
+		if (f) break;
+
+		static_buff[strlen(static_buff) - 4] = 0;
+		strcat(static_buff, ".zip");
+		f = fopen(static_buff, "rb");
+		if (f) break;
+	}
+
+	/** Check Bios files in <rom path>/bios */
+	if(!f){
+		for (i = 0; i < count; i++)
+		{
+			sprintf(static_buff, "%s/bios/%s", mRomPath, files[i]);
+			printf("static_buff: %s\n", static_buff);
+			strcat(static_buff, ".bin");
+			f = fopen(static_buff, "rb");
+			if (f) break;
+
+			static_buff[strlen(static_buff) - 4] = 0;
+			strcat(static_buff, ".zip");
+			f = fopen(static_buff, "rb");
+			if (f) break;
+		}
+	}
+
+	/** Check Bios files in rom path */
+	if(!f){
+		for (i = 0; i < count; i++)
+		{
+			sprintf(static_buff, "%s/%s", mRomPath, files[i]);
+			printf("static_buff: %s\n", static_buff);
+			strcat(static_buff, ".bin");
+			f = fopen(static_buff, "rb");
+			if (f) break;
+
+			static_buff[strlen(static_buff) - 4] = 0;
+			strcat(static_buff, ".zip");
+			f = fopen(static_buff, "rb");
+			if (f) break;
+		}
+	}
+
+	if (f) {
+		lprintf("using bios: %s\n", static_buff);
+		fclose(f);
+		return static_buff;
+	} else {
+		char* country;
+		char** bios_names;
+		if(*region == 4){ //USA
+			country = "USA";
+			bios_names = biosfiles_us;
+		}
+		else if(*region == 8){ //EU
+			country = "EU";
+			bios_names = biosfiles_eu;
+		}
+		else{ //JAP
+			country = "JAP";
+			bios_names = biosfiles_jp;
+		}
+
+		sprintf(static_buff, "no %s BIOS files found, read docs", country);
+		menu_update_msg(static_buff);
+
+		/** Set notif for BIOS */
+	    char shell_cmd[400];
+	    sprintf(shell_cmd, "%s 0 \"    %s BIOS FILE MISSING^^Connect your FunKey S to ^your computer and copy the^BIOS file in the folder:^    Sega Genesis/bios/^^The file can be called:^ - %s.bin^ - %s.bin^ - %s.bin^ - %s.bin^^For more instructions:^www.funkey-project.com^^Press any button to exit...\"",
+	            SHELL_CMD_NOTIF_SET, country, bios_names[0], bios_names[1], bios_names[2], bios_names[3]);
+	    FILE *fp = popen(shell_cmd, "r");
+	    if (fp == NULL) {
+	        printf("In %s, Failed to run command %s\n", __func__, shell_cmd);
+	    } else {
+	        pclose(fp);
+	    }
+
+	    /** Wait for key press */
+	    SDL_Event event;
+	    while(event.type != SDL_KEYUP && event.type != SDL_QUIT){
+	        while (SDL_PollEvent(&event))
+			SDL_Delay(60);
+	    }
+
+	    /** Clear notif for BIOS */
+	    fp = popen(SHELL_CMD_NOTIF_CLEAR, "r");
+	    if (fp == NULL) {
+	        printf("In %s, Failed to run command %s\n", __func__, SHELL_CMD_NOTIF_CLEAR);
+	    } else {
+	        pclose(fp);
+	    }
+
+	    /** Force clean exit */
+        //engineState = PGS_Quit;
+		emu_finish();
+		plat_finish();
+		plat_target_finish();
+		exit(0);
+		return NULL;
+	}
+}
+
+/* check if the name begins with BIOS name */
+/*
+static int emu_isBios(const char *name)
+{
+	int i;
+	for (i = 0; i < sizeof(biosfiles_us)/sizeof(biosfiles_us[0]); i++)
+		if (strstr(name, biosfiles_us[i]) != NULL) return 1;
+	for (i = 0; i < sizeof(biosfiles_eu)/sizeof(biosfiles_eu[0]); i++)
+		if (strstr(name, biosfiles_eu[i]) != NULL) return 1;
+	for (i = 0; i < sizeof(biosfiles_jp)/sizeof(biosfiles_jp[0]); i++)
+		if (strstr(name, biosfiles_jp[i]) != NULL) return 1;
+	return 0;
+}
+*/
+
+static int extract_text(char *dest, const unsigned char *src, int len, int swab)
+{
+	char *p = dest;
+	int i;
+
+	if (swab) swab = 1;
+
+	for (i = len - 1; i >= 0; i--)
+	{
+		if (src[i^swab] != ' ') break;
+	}
+	len = i + 1;
+
+	for (i = 0; i < len; i++)
+	{
+		unsigned char s = src[i^swab];
+		if (s >= 0x20 && s < 0x7f && s != '#' && s != '|' &&
+			s != '[' && s != ']' && s != '\\')
+		{
+			*p++ = s;
+		}
+		else
+		{
+			sprintf(p, "\\%02x", s);
+			p += 3;
+		}
+	}
+
+	return p - dest;
+}
+
+static char *emu_make_rom_id(const char *fname)
+{
+	static char id_string[3+0xe*3+0x3*3+0x30*3+3];
+	int pos, swab = 1;
+
+	if (PicoIn.AHW & PAHW_MCD) {
+		strcpy(id_string, "CD|");
+		swab = 0;
+	}
+	else if (PicoIn.AHW & PAHW_SMS)
+		strcpy(id_string, "MS|");
+	else	strcpy(id_string, "MD|");
+	pos = 3;
+
+	if (!(PicoIn.AHW & PAHW_SMS)) {
+		pos += extract_text(id_string + pos, media_id_header + 0x80, 0x0e, swab); // serial
+		id_string[pos] = '|'; pos++;
+		pos += extract_text(id_string + pos, media_id_header + 0xf0, 0x03, swab); // region
+		id_string[pos] = '|'; pos++;
+		pos += extract_text(id_string + pos, media_id_header + 0x50, 0x30, swab); // overseas name
+		id_string[pos] = 0;
+		if (pos > 5)
+			return id_string;
+		pos = 3;
+	}
+
+	// can't find name in ROM, use filename
+	fname_ext(id_string + 3, sizeof(id_string) - 3, NULL, NULL, fname);
+
+	return id_string;
+}
+
+// buffer must be at least 150 byte long
+void emu_get_game_name(char *str150)
+{
+	int ret, swab = (PicoIn.AHW & PAHW_MCD) ? 0 : 1;
+	char *s, *d;
+
+	ret = extract_text(str150, media_id_header + 0x50, 0x30, swab); // overseas name
+
+	for (s = d = str150 + 1; s < str150+ret; s++)
+	{
+		if (*s == 0) break;
+		if (*s != ' ' || d[-1] != ' ')
+			*d++ = *s;
+	}
+	*d = 0;
+}
+
+static void system_announce(void)
+{
+	const char *sys_name, *tv_standard, *extra = "";
+	int fps;
+
+	if (PicoIn.AHW & PAHW_SMS) {
+		sys_name = "Master System";
+#ifdef NO_SMS
+		extra = " [no support]";
+#endif
+	} else if (PicoIn.AHW & PAHW_PICO) {
+		sys_name = "Pico";
+	} else if ((PicoIn.AHW & (PAHW_32X|PAHW_MCD)) == (PAHW_32X|PAHW_MCD)) {
+		sys_name = "32X + Mega CD";
+		if ((Pico.m.hardware & 0xc0) == 0x80)
+			sys_name = "32X + Sega CD";
+	} else if (PicoIn.AHW & PAHW_MCD) {
+		sys_name = "Mega CD";
+		if ((Pico.m.hardware & 0xc0) == 0x80)
+			sys_name = "Sega CD";
+	} else if (PicoIn.AHW & PAHW_32X) {
+		sys_name = "32X";
+	} else {
+		sys_name = "MegaDrive";
+		if ((Pico.m.hardware & 0xc0) == 0x80)
+			sys_name = "Genesis";
+	}
+	tv_standard = Pico.m.pal ? "PAL" : "NTSC";
+	fps = Pico.m.pal ? 50 : 60;
+
+	//emu_status_msg("%s %s / %dFPS%s", tv_standard, sys_name, fps, extra);
+	printf("\nSystem Announce: %s, %s / %dFPS%s\n", sys_name, tv_standard, fps, extra);
+	printf("PicoIn.AHW = %d, Pico.m.hardware=%d\n", PicoIn.AHW, Pico.m.hardware);
+}
+
+static void do_region_override(const char *media_fname)
+{
+	// we only need to override region if config tells us so
+	int ret = emu_read_config(media_fname, 0);
+	if (!ret) emu_read_config(NULL, 0);
+}
+
+int emu_reload_rom(const char *rom_fname_in)
+{
+	// use setting before rom config is loaded
+	int autoload = g_autostateld_opt;
+	char *rom_fname = NULL;
+	char ext[5];
+	enum media_type_e media_type;
+	int menu_romload_started = 0;
+	char carthw_path[512];
+	int retval = 0;
+
+	lprintf("emu_ReloadRom(%s)\n", rom_fname_in);
+
+	rom_fname = strdup(rom_fname_in);
+	if (rom_fname == NULL)
+		return 0;
+
+	get_ext(rom_fname, ext);
+
+	// early cleanup
+	PicoPatchUnload();
+	if (movie_data) {
+		free(movie_data);
+		movie_data = 0;
+	}
+
+	if (!strcmp(ext, ".gmv"))
+	{
+		// check for both gmv and rom
+		int dummy;
+		FILE *movie_file = fopen(rom_fname, "rb");
+		if (!movie_file) {
+			menu_update_msg("Failed to open movie.");
+			goto out;
+		}
+		fseek(movie_file, 0, SEEK_END);
+		movie_size = ftell(movie_file);
+		fseek(movie_file, 0, SEEK_SET);
+		if (movie_size < 64+3) {
+			menu_update_msg("Invalid GMV file.");
+			fclose(movie_file);
+			goto out;
+		}
+		movie_data = malloc(movie_size);
+		if (movie_data == NULL) {
+			menu_update_msg("low memory.");
+			fclose(movie_file);
+			goto out;
+		}
+		dummy = fread(movie_data, 1, movie_size, movie_file);
+		fclose(movie_file);
+		if (strncmp((char *)movie_data, "Gens Movie TEST", 15) != 0) {
+			menu_update_msg("Invalid GMV file.");
+			goto out;
+		}
+		dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);
+		if (!dummy) {
+			menu_update_msg("Could't find a ROM for movie.");
+			goto out;
+		}
+		get_ext(rom_fname, ext);
+		lprintf("gmv loaded for %s\n", rom_fname);
+	}
+	else if (!strcmp(ext, ".pat"))
+	{
+		int dummy;
+		PicoPatchLoad(rom_fname);
+		dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);
+		if (!dummy) {
+			menu_update_msg("Could't find a ROM to patch.");
+			goto out;
+		}
+		get_ext(rom_fname, ext);
+	}
+
+	menu_romload_prepare(rom_fname); // also CD load
+	menu_romload_started = 1;
+
+	emu_make_path(carthw_path, "carthw.cfg", sizeof(carthw_path));
+
+	media_type = PicoLoadMedia(rom_fname, carthw_path,
+			find_bios, do_region_override);
+
+	switch (media_type) {
+	case PM_BAD_DETECT:
+		menu_update_msg("Not a ROM/CD img selected.");
+		goto out;
+	case PM_BAD_CD:
+		menu_update_msg("Invalid CD image");
+		goto out;
+	case PM_BAD_CD_NO_BIOS:
+		// find_bios() prints a message
+		goto out;
+	case PM_ERROR:
+		menu_update_msg("Load error");
+		goto out;
+	default:
+		break;
+	}
+
+	// make quirks visible in UI
+	if (PicoIn.quirks & PQUIRK_FORCE_6BTN)
+		currentConfig.input_dev0 = PICO_INPUT_PAD_6BTN;
+
+	menu_romload_end();
+	menu_romload_started = 0;
+
+	if (PicoPatches) {
+		PicoPatchPrepare();
+		PicoPatchApply();
+	}
+
+	// additional movie stuff
+	if (movie_data)
+	{
+		enum input_device indev = (movie_data[0x14] == '6') ?
+			PICO_INPUT_PAD_6BTN : PICO_INPUT_PAD_3BTN;
+		PicoSetInputDevice(0, indev);
+		PicoSetInputDevice(1, indev);
+
+		PicoIn.opt |= POPT_DIS_VDP_FIFO; // no VDP fifo timing
+		if (movie_data[0xF] >= 'A') {
+			if (movie_data[0x16] & 0x80) {
+				PicoIn.regionOverride = 8;
+			} else {
+				PicoIn.regionOverride = 4;
+			}
+			PicoReset();
+			// TODO: bits 6 & 5
+		}
+		movie_data[0x18+30] = 0;
+		emu_status_msg("MOVIE: %s", (char *) &movie_data[0x18]);
+	}
+	else
+	{
+		system_announce();
+		PicoIn.opt &= ~POPT_DIS_VDP_FIFO;
+	}
+
+	/* Set input map */
+	if (PicoIn.AHW & PAHW_SMS) {
+		printf("plat set sms input\n");
+		plat_set_sms_input();
+	}
+	else{
+		plat_set_genesis_input();
+	}
+
+	strncpy(rom_fname_loaded, rom_fname, sizeof(rom_fname_loaded)-1);
+	rom_fname_loaded[sizeof(rom_fname_loaded)-1] = 0;
+
+	// load SRAM for this ROM
+	if (currentConfig.EmuOpt & EOPT_EN_SRAM)
+		emu_save_load_game(1, 1);
+
+	// state autoload?
+	if (autoload) {
+		int time, newest = 0, newest_slot = -1;
+		int slot;
+
+		for (slot = 0; slot < 10; slot++) {
+			if (emu_check_save_file(slot, &time)) {
+				if (time > newest) {
+					newest = time;
+					newest_slot = slot;
+				}
+			}
+		}
+
+		if (newest_slot >= 0) {
+			lprintf("autoload slot %d\n", newest_slot);
+			state_slot = newest_slot;
+			emu_save_load_game(1, 0);
+		}
+		else {
+			lprintf("no save to autoload.\n");
+		}
+	}
+
+	retval = 1;
+out:
+	if (menu_romload_started)
+		menu_romload_end();
+	free(rom_fname);
+	return retval;
+}
+
+int emu_swap_cd(const char *fname)
+{
+	enum cd_track_type cd_type;
+	int ret = -1;
+
+	cd_type = PicoCdCheck(fname, NULL);
+	if (cd_type != CT_UNKNOWN)
+		ret = cdd_load(fname, cd_type);
+	if (ret != 0) {
+		menu_update_msg("Load failed, invalid CD image?");
+		return 0;
+	}
+
+	strncpy(rom_fname_loaded, fname, sizeof(rom_fname_loaded)-1);
+	rom_fname_loaded[sizeof(rom_fname_loaded) - 1] = 0;
+
+	return 1;
+}
+
+// <base dir><end>
+void emu_make_path(char *buff, const char *end, int size)
+{
+	int pos, end_len;
+
+	end_len = strlen(end);
+	pos = plat_get_root_dir(buff, size);
+	strncpy(buff + pos, end, size - pos);
+	buff[size - 1] = 0;
+	if (pos + end_len > size - 1)
+		lprintf("Warning: path truncated: %s\n", buff);
+}
+
+static void make_config_cfg(char *cfg_buff_512)
+{
+	emu_make_path(cfg_buff_512, PicoConfigFile, 512-6);
+	if (config_slot != 0)
+	{
+		char *p = strrchr(cfg_buff_512, '.');
+		if (p == NULL)
+			p = cfg_buff_512 + strlen(cfg_buff_512);
+		sprintf(p, ".%i.cfg", config_slot);
+	}
+	cfg_buff_512[511] = 0;
+}
+
+void emu_prep_defconfig(void)
+{
+	memset(&defaultConfig, 0, sizeof(defaultConfig));
+	defaultConfig.EmuOpt    = EOPT_EN_SRAM | EOPT_EN_SOUND | EOPT_16BPP |
+				  EOPT_GZIP_SAVES | 0x10/*?*/;
+	defaultConfig.s_PicoOpt = POPT_EN_YM2413|POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80 |
+				  POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX |
+				  POPT_EN_DRC|POPT_ACC_SPRITES |
+				  POPT_EN_32X|POPT_EN_PWM;
+	defaultConfig.s_PsndRate = 44100;
+	defaultConfig.s_PicoRegion = 0; // auto
+	defaultConfig.s_PicoAutoRgnOrder = 0x184; // US, EU, JP
+	defaultConfig.s_PicoCDBuffers = 0;
+	defaultConfig.s_PicoSndFilterAlpha = 0x10000 * 60 / 100;
+	defaultConfig.confirm_save = EOPT_CONFIRM_SAVE;
+	defaultConfig.Frameskip = -1; // auto
+	defaultConfig.input_dev0 = PICO_INPUT_PAD_3BTN;
+	defaultConfig.input_dev1 = PICO_INPUT_PAD_3BTN;
+	defaultConfig.volume = 50;
+	//defaultConfig.volume = 99;
+	defaultConfig.gamma = 100;
+	defaultConfig.scaling = 0;
+	defaultConfig.turbo_rate = 15;
+	defaultConfig.msh2_khz = PICO_MSH2_HZ / 1000;
+	defaultConfig.ssh2_khz = PICO_SSH2_HZ / 1000;
+	defaultConfig.max_skip = 4;
+
+	// platform specific overrides
+	pemu_prep_defconfig();
+}
+
+void emu_set_defconfig(void)
+{
+	memcpy(&currentConfig, &defaultConfig, sizeof(currentConfig));
+	PicoIn.opt = currentConfig.s_PicoOpt;
+	PicoIn.sndRate = currentConfig.s_PsndRate;
+	PicoIn.regionOverride = currentConfig.s_PicoRegion;
+	PicoIn.autoRgnOrder = currentConfig.s_PicoAutoRgnOrder;
+	PicoIn.sndFilterAlpha = currentConfig.s_PicoSndFilterAlpha;
+}
+
+int emu_read_config(const char *rom_fname, int no_defaults)
+{
+	char cfg[512];
+	int ret;
+
+	if (!no_defaults)
+		emu_set_defconfig();
+
+	if (rom_fname == NULL)
+	{
+		// global config
+		make_config_cfg(cfg);
+		ret = config_readsect(cfg, NULL);
+	}
+	else
+	{
+		char ext[16];
+		int vol;
+
+		if (config_slot != 0)
+			snprintf(ext, sizeof(ext), ".%i.cfg", config_slot);
+		else
+			strcpy(ext, ".cfg");
+
+		fname_ext(cfg, sizeof(cfg), "cfg"PATH_SEP, ext, rom_fname); // gamepath/gamename.cfg
+
+		// read user's config
+		vol = currentConfig.volume;
+		ret = config_readsect(cfg, NULL);
+		currentConfig.volume = vol; // make vol global (bah)
+
+		if (ret != 0)
+		{
+			// read global config, and apply game_def.cfg on top
+			make_config_cfg(cfg); // $HOME/.picodrive/config2.cfg
+			config_readsect(cfg, NULL);
+
+			emu_make_path(cfg, "game_def.cfg", sizeof(cfg)); // $HOME/.picodrive/game_def.cfg
+			ret = config_readsect(cfg, emu_make_rom_id(rom_fname));
+		}
+	}
+
+	pemu_validate_config();
+	PicoIn.overclockM68k = currentConfig.overclock_68k;
+
+	// some sanity checks
+	if (currentConfig.volume < 0 || currentConfig.volume > 99)
+		currentConfig.volume = 50;
+
+	if (ret == 0)
+		config_slot_current = config_slot;
+
+	return (ret == 0);
+}
+
+
+int emu_write_config(int is_game)
+{
+	char cfg[512];
+	int ret, write_lrom = 0;
+
+	if (!is_game)
+	{
+		make_config_cfg(cfg);
+		write_lrom = 1;
+	} else {
+		char ext[16];
+
+		if (config_slot != 0)
+			snprintf(ext, sizeof(ext), ".%i.cfg", config_slot);
+		else
+			strcpy(ext, ".cfg");
+
+		romfname_ext(cfg, sizeof(cfg), "cfg"PATH_SEP, ext);
+	}
+
+	lprintf("emu_write_config: %s ", cfg);
+	ret = config_write(cfg);
+	if (write_lrom) config_writelrom(cfg);
+#ifdef __GP2X__
+	sync();
+#endif
+	lprintf((ret == 0) ? "(ok)\n" : "(failed)\n");
+
+	if (ret == 0) config_slot_current = config_slot;
+	return ret == 0;
+}
+
+
+/* always using built-in font */
+
+#define mk_text_out(name, type, val, topleft, step_x, step_y) \
+void name(int x, int y, const char *text)				\
+{									\
+	int i, l, len = strlen(text);					\
+	type *screen = (type *)(topleft) + x * step_x + y * step_y;	\
+									\
+	for (i = 0; i < len; i++, screen += 8 * step_x)			\
+	{								\
+		for (l = 0; l < 8; l++)					\
+		{							\
+			unsigned char fd = fontdata8x8[text[i] * 8 + l];\
+			type *s = screen + l * step_y;			\
+			if (fd&0x80) s[step_x * 0] = val;		\
+			if (fd&0x40) s[step_x * 1] = val;		\
+			if (fd&0x20) s[step_x * 2] = val;		\
+			if (fd&0x10) s[step_x * 3] = val;		\
+			if (fd&0x08) s[step_x * 4] = val;		\
+			if (fd&0x04) s[step_x * 5] = val;		\
+			if (fd&0x02) s[step_x * 6] = val;		\
+			if (fd&0x01) s[step_x * 7] = val;		\
+		}							\
+	}								\
+}
+
+mk_text_out(emu_text_out8,      unsigned char,    0xf0, g_screen_ptr, 1, g_screen_ppitch)
+mk_text_out(emu_text_out16,     unsigned short, 0xffff, g_screen_ptr, 1, g_screen_ppitch)
+mk_text_out(emu_text_out8_rot,  unsigned char,    0xf0,
+	(char *)g_screen_ptr  + (g_screen_ppitch - 1) * g_screen_height, -g_screen_height, 1)
+mk_text_out(emu_text_out16_rot, unsigned short, 0xffff,
+	(short *)g_screen_ptr + (g_screen_ppitch - 1) * g_screen_height, -g_screen_height, 1)
+
+#undef mk_text_out
+
+void emu_osd_text16(int x, int y, const char *text)
+{
+	int len = strlen(text) * 8;
+	int i, h;
+
+	len++;
+	if (x + len > g_screen_width)
+		len = g_screen_width - x;
+
+	for (h = 0; h < 8; h++) {
+		unsigned short *p;
+		p = (unsigned short *)g_screen_ptr
+			+ x + g_screen_ppitch * (y + h);
+		for (i = len; i > 0; i--, p++)
+			*p = (*p >> 2) & 0x39e7;
+	}
+	emu_text_out16(x, y, text);
+}
+
+static void update_movie(void)
+{
+	int offs = Pico.m.frame_count*3 + 0x40;
+	if (offs+3 > movie_size) {
+		free(movie_data);
+		movie_data = 0;
+		emu_status_msg("END OF MOVIE.");
+		lprintf("END OF MOVIE.\n");
+	} else {
+		// MXYZ SACB RLDU
+		PicoIn.pad[0] = ~movie_data[offs]   & 0x8f; // ! SCBA RLDU
+		if(!(movie_data[offs]   & 0x10)) PicoIn.pad[0] |= 0x40; // C
+		if(!(movie_data[offs]   & 0x20)) PicoIn.pad[0] |= 0x10; // A
+		if(!(movie_data[offs]   & 0x40)) PicoIn.pad[0] |= 0x20; // B
+		PicoIn.pad[1] = ~movie_data[offs+1] & 0x8f; // ! SCBA RLDU
+		if(!(movie_data[offs+1] & 0x10)) PicoIn.pad[1] |= 0x40; // C
+		if(!(movie_data[offs+1] & 0x20)) PicoIn.pad[1] |= 0x10; // A
+		if(!(movie_data[offs+1] & 0x40)) PicoIn.pad[1] |= 0x20; // B
+		PicoIn.pad[0] |= (~movie_data[offs+2] & 0x0A) << 8; // ! MZYX
+		if(!(movie_data[offs+2] & 0x01)) PicoIn.pad[0] |= 0x0400; // X
+		if(!(movie_data[offs+2] & 0x04)) PicoIn.pad[0] |= 0x0100; // Z
+		PicoIn.pad[1] |= (~movie_data[offs+2] & 0xA0) << 4; // ! MZYX
+		if(!(movie_data[offs+2] & 0x10)) PicoIn.pad[1] |= 0x0400; // X
+		if(!(movie_data[offs+2] & 0x40)) PicoIn.pad[1] |= 0x0100; // Z
+	}
+}
+
+static int try_ropen_file(const char *fname, int *time)
+{
+	struct stat st;
+	FILE *f;
+
+	f = fopen(fname, "rb");
+	if (f) {
+		if (time != NULL) {
+			*time = 0;
+			if (fstat(fileno(f), &st) == 0)
+				*time = (int)st.st_mtime;
+		}
+		fclose(f);
+		return 1;
+	}
+	return 0;
+}
+
+char *emu_get_save_fname(int load, int is_sram, int slot, int *time)
+{
+	char *saveFname = static_buff;
+	char ext[16];
+
+	if (is_sram)
+	{
+		strcpy(ext, (PicoIn.AHW & PAHW_MCD) ? ".brm" : ".srm");
+		romfname_ext(saveFname, sizeof(static_buff),
+			(PicoIn.AHW & PAHW_MCD) ? "brm"PATH_SEP : "srm"PATH_SEP, ext);
+		if (!load)
+			return saveFname;
+
+		if (try_ropen_file(saveFname, time))
+			return saveFname;
+
+		romfname_ext(saveFname, sizeof(static_buff), NULL, ext);
+		if (try_ropen_file(saveFname, time))
+			return saveFname;
+	}
+	else
+	{
+		const char *ext_main = (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds.gz" : ".mds";
+		const char *ext_othr = (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds" : ".mds.gz";
+		ext[0] = 0;
+		if (slot > 0 && slot < 10)
+			sprintf(ext, ".%i", slot);
+		strcat(ext, ext_main);
+
+		if (!load) {
+			romfname_ext(saveFname, sizeof(static_buff), "mds" PATH_SEP, ext);
+			return saveFname;
+		}
+		else {
+			romfname_ext(saveFname, sizeof(static_buff), "mds" PATH_SEP, ext);
+			if (try_ropen_file(saveFname, time))
+				return saveFname;
+
+			romfname_ext(saveFname, sizeof(static_buff), NULL, ext);
+			if (try_ropen_file(saveFname, time))
+				return saveFname;
+
+			// try the other ext
+			ext[0] = 0;
+			if (slot > 0 && slot < 10)
+				sprintf(ext, ".%i", slot);
+			strcat(ext, ext_othr);
+
+			romfname_ext(saveFname, sizeof(static_buff), "mds"PATH_SEP, ext);
+			if (try_ropen_file(saveFname, time))
+				return saveFname;
+		}
+	}
+
+	return NULL;
+}
+
+int emu_check_save_file(int slot, int *time)
+{
+	return emu_get_save_fname(1, 0, slot, time) ? 1 : 0;
+}
+
+int emu_save_load_game_from_file(int load, char *saveFname){
+	int ret = PicoState(saveFname, !load);
+	if (!ret) {
+		//emu_status_msg(load ? "STATE LOADED" : "STATE SAVED");
+	} else {
+		//emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+int emu_save_load_game(int load, int sram)
+{
+	int ret = 0;
+	char *saveFname;
+
+	// make save filename
+	saveFname = emu_get_save_fname(load, sram, state_slot, NULL);
+	if (saveFname == NULL) {
+		/*if (!sram)
+			emu_status_msg(load ? "LOAD FAILED (missing file)" : "SAVE FAILED");*/
+		return -1;
+	}
+
+	lprintf("saveLoad (%i, %i): %s\n", load, sram, saveFname);
+
+	if (sram)
+	{
+		FILE *sramFile;
+		int sram_size;
+		unsigned char *sram_data;
+		int truncate = 1;
+		if (PicoIn.AHW & PAHW_MCD)
+		{
+			if (PicoIn.opt & POPT_EN_MCD_RAMCART) {
+				sram_size = 0x12000;
+				sram_data = Pico.sv.data;
+				if (sram_data)
+					memcpy(sram_data, Pico_mcd->bram, 0x2000);
+			} else {
+				sram_size = 0x2000;
+				sram_data = Pico_mcd->bram;
+				truncate  = 0; // the .brm may contain RAM cart data after normal brm
+			}
+		} else {
+			sram_size = Pico.sv.size;
+			sram_data = Pico.sv.data;
+		}
+		if (sram_data == NULL)
+			return 0; // cart saves forcefully disabled for this game
+
+		if (load)
+		{
+			sramFile = fopen(saveFname, "rb");
+			if (!sramFile)
+				return -1;
+			ret = fread(sram_data, 1, sram_size, sramFile);
+			ret = ret > 0 ? 0 : -1;
+			fclose(sramFile);
+			if ((PicoIn.AHW & PAHW_MCD) && (PicoIn.opt&POPT_EN_MCD_RAMCART))
+				memcpy(Pico_mcd->bram, sram_data, 0x2000);
+		} else {
+			// sram save needs some special processing
+			// see if we have anything to save
+			for (; sram_size > 0; sram_size--)
+				if (sram_data[sram_size-1]) break;
+
+			if (sram_size) {
+				sramFile = fopen(saveFname, truncate ? "wb" : "r+b");
+				if (!sramFile) sramFile = fopen(saveFname, "wb"); // retry
+				if (!sramFile) return -1;
+				ret = fwrite(sram_data, 1, sram_size, sramFile);
+				ret = (ret != sram_size) ? -1 : 0;
+				fclose(sramFile);
+#ifdef __GP2X__
+				sync();
+#endif
+			}
+		}
+		return ret;
+	}
+	else
+	{
+		ret = PicoState(saveFname, !load);
+		if (!ret) {
+#ifdef __GP2X__
+			if (!load) sync();
+#endif
+			//emu_status_msg(load ? "STATE LOADED" : "STATE SAVED");
+		} else {
+			//emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");
+			ret = -1;
+		}
+
+		return ret;
+	}
+}
+
+void emu_set_fastforward(int set_on)
+{
+	static void *set_PsndOut = NULL;
+	static int set_Frameskip, set_EmuOpt, is_on = 0;
+
+	if (set_on && !is_on) {
+		set_PsndOut = PicoIn.sndOut;
+		set_Frameskip = currentConfig.Frameskip;
+		set_EmuOpt = currentConfig.EmuOpt;
+		PicoIn.sndOut = NULL;
+		currentConfig.Frameskip = 8;
+		currentConfig.EmuOpt &= ~4;
+		currentConfig.EmuOpt |= EOPT_NO_FRMLIMIT;
+		is_on = 1;
+		emu_status_msg("FAST FORWARD");
+	}
+	else if (!set_on && is_on) {
+		PicoIn.sndOut = set_PsndOut;
+		currentConfig.Frameskip = set_Frameskip;
+		currentConfig.EmuOpt = set_EmuOpt;
+		PsndRerate(1);
+		is_on = 0;
+		// mainly to unbreak pcm
+		if (PicoIn.AHW & PAHW_MCD)
+			pcd_state_loaded();
+	}
+}
+
+static void emu_tray_open(void)
+{
+	engineState = PGS_TrayMenu;
+}
+
+static void emu_tray_close(void)
+{
+	emu_status_msg("CD tray closed.");
+}
+
+void emu_32x_startup(void)
+{
+	plat_video_toggle_renderer(0, 0); // HACK
+	system_announce();
+}
+
+void emu_reset_game(void)
+{
+	PicoReset();
+	reset_timing = 1;
+}
+
+void run_events_pico(unsigned int events)
+{
+	int lim_x;
+
+	if (events & PEV_PICO_SWINP) {
+		pico_inp_mode++;
+		if (pico_inp_mode > 2)
+			pico_inp_mode = 0;
+		switch (pico_inp_mode) {
+			case 2: emu_status_msg("Input: Pen on Pad"); break;
+			case 1: emu_status_msg("Input: Pen on Storyware"); break;
+			case 0: emu_status_msg("Input: Joystick");
+				PicoPicohw.pen_pos[0] = PicoPicohw.pen_pos[1] = 0x8000;
+				break;
+		}
+	}
+	if (events & PEV_PICO_PPREV) {
+		PicoPicohw.page--;
+		if (PicoPicohw.page < 0)
+			PicoPicohw.page = 0;
+		emu_status_msg("Page %i", PicoPicohw.page);
+	}
+	if (events & PEV_PICO_PNEXT) {
+		PicoPicohw.page++;
+		if (PicoPicohw.page > 6)
+			PicoPicohw.page = 6;
+		emu_status_msg("Page %i", PicoPicohw.page);
+	}
+
+	if (pico_inp_mode == 0)
+		return;
+
+	/* handle other input modes */
+	if (PicoIn.pad[0] & 1) pico_pen_y--;
+	if (PicoIn.pad[0] & 2) pico_pen_y++;
+	if (PicoIn.pad[0] & 4) pico_pen_x--;
+	if (PicoIn.pad[0] & 8) pico_pen_x++;
+	PicoIn.pad[0] &= ~0x0f; // release UDLR
+
+	lim_x = (Pico.video.reg[12]&1) ? 319 : 255;
+	if (pico_pen_y < 8)
+		pico_pen_y = 8;
+	if (pico_pen_y > 224 - PICO_PEN_ADJUST_Y)
+		pico_pen_y = 224 - PICO_PEN_ADJUST_Y;
+	if (pico_pen_x < 0)
+		pico_pen_x = 0;
+	if (pico_pen_x > lim_x - PICO_PEN_ADJUST_X)
+		pico_pen_x = lim_x - PICO_PEN_ADJUST_X;
+
+	PicoPicohw.pen_pos[0] = pico_pen_x;
+	if (!(Pico.video.reg[12] & 1))
+		PicoPicohw.pen_pos[0] += pico_pen_x / 4;
+	PicoPicohw.pen_pos[0] += 0x3c;
+	PicoPicohw.pen_pos[1] = pico_inp_mode == 1 ? (0x2f8 + pico_pen_y) : (0x1fc + pico_pen_y);
+}
+
+static void do_turbo(unsigned short *pad, int acts)
+{
+	static int turbo_pad = 0;
+	static unsigned char turbo_cnt[3] = { 0, 0, 0 };
+	int inc = currentConfig.turbo_rate * 2;
+
+	if (acts & 0x1000) {
+		turbo_cnt[0] += inc;
+		if (turbo_cnt[0] >= 60)
+			turbo_pad ^= 0x10, turbo_cnt[0] = 0;
+	}
+	if (acts & 0x2000) {
+		turbo_cnt[1] += inc;
+		if (turbo_cnt[1] >= 60)
+			turbo_pad ^= 0x20, turbo_cnt[1] = 0;
+	}
+	if (acts & 0x4000) {
+		turbo_cnt[2] += inc;
+		if (turbo_cnt[2] >= 60)
+			turbo_pad ^= 0x40, turbo_cnt[2] = 0;
+	}
+	*pad |= turbo_pad & (acts >> 8);
+}
+
+static void run_events_ui(unsigned int which)
+{
+	char shell_cmd[100];
+	FILE *fp;
+	//emu_action_old = emu_action;
+	//printf("New event: %d\n", which);
+
+	if (which & (PEV_STATE_LOAD|PEV_STATE_SAVE))
+	{
+		int do_it = 1;
+		if ( emu_check_save_file(state_slot, NULL) &&
+			(((which & PEV_STATE_LOAD) && (currentConfig.confirm_save & EOPT_CONFIRM_LOAD)) ||
+			 ((which & PEV_STATE_SAVE) && (currentConfig.confirm_save & EOPT_CONFIRM_SAVE))) )
+		{
+			const char *nm;
+			char tmp[64];
+			int keys, len;
+
+			strcpy(tmp, (which & PEV_STATE_LOAD) ? "LOAD STATE? " : "OVERWRITE SAVE? ");
+			len = strlen(tmp);
+			nm = in_get_key_name(-1, -PBTN_MOK);
+			snprintf(tmp + len, sizeof(tmp) - len, "(%s=yes, ", nm);
+			len = strlen(tmp);
+			nm = in_get_key_name(-1, -PBTN_MBACK);
+			snprintf(tmp + len, sizeof(tmp) - len, "%s=no)", nm);
+
+			plat_status_msg_busy_first(tmp);
+
+			in_set_config_int(0, IN_CFG_BLOCKING, 1);
+			while (in_menu_wait_any(NULL, 50) & (PBTN_MOK | PBTN_MBACK))
+				;
+			while ( !((keys = in_menu_wait_any(NULL, 50)) & (PBTN_MOK | PBTN_MBACK)))
+				;
+			if (keys & PBTN_MBACK)
+				do_it = 0;
+			while (in_menu_wait_any(NULL, 50) & (PBTN_MOK | PBTN_MBACK))
+				;
+			in_set_config_int(0, IN_CFG_BLOCKING, 0);
+			plat_status_msg_clear();
+		}
+		if (do_it) {
+			plat_status_msg_busy_first((which & PEV_STATE_LOAD) ? "LOADING STATE" : "SAVING STATE");
+			PicoStateProgressCB = plat_status_msg_busy_next;
+			emu_save_load_game((which & PEV_STATE_LOAD) ? 1 : 0, 0);
+			PicoStateProgressCB = NULL;
+		}
+	}
+	if (which & PEV_SWITCH_RND)
+	{
+		plat_video_toggle_renderer(1, 0);
+	}
+	if (which & PEV_VOL_DOWN)
+	{
+		printf("PEV_VOL_DOWN\r\n");
+		/// ----- Compute new value -----
+		volume_percentage = (volume_percentage < STEP_CHANGE_VOLUME)?
+			0:(volume_percentage-STEP_CHANGE_VOLUME);
+		/// ----- HUD msg ------
+		char txt[100];
+		sprintf(txt, "VOLUME %d%%", volume_percentage);
+		plat_status_msg_busy_first(txt);
+		/// ----- Shell cmd ----
+		sprintf(shell_cmd, "%s %d", SHELL_CMD_VOLUME_SET, volume_percentage);
+		fp = popen(shell_cmd, "r");
+		if (fp == NULL) {
+			printf("Failed to run command %s\n", shell_cmd);
+		} else {
+	        	pclose(fp);
+		}
+	}
+	if (which & PEV_VOL_UP)
+	{
+		printf("PEV_VOL_UP\r\n");
+		/// ----- Compute new value -----
+		volume_percentage = (volume_percentage > 100 - STEP_CHANGE_VOLUME)?
+			100:(volume_percentage+STEP_CHANGE_VOLUME);
+		/// ----- HUD msg ------
+		char txt[100];
+		sprintf(txt, "VOLUME %d%%", volume_percentage);
+		plat_status_msg_busy_first(txt);
+		/// ----- Shell cmd ----
+		sprintf(shell_cmd, "%s %d", SHELL_CMD_VOLUME_SET, volume_percentage);
+		fp = popen(shell_cmd, "r");
+		if (fp == NULL) {
+			printf("Failed to run command %s\n", shell_cmd);
+		} else {
+	        	pclose(fp);
+		}
+	}
+	if (which & PEV_BRIGHT_UP)
+	{
+		printf("PEV_BRIGHT_UP\r\n");
+		/// ----- Compute new value -----
+		brightness_percentage = (brightness_percentage > 100 - STEP_CHANGE_BRIGHTNESS)?
+			100:(brightness_percentage+STEP_CHANGE_BRIGHTNESS);
+		/// ----- HUD msg ------
+		char txt[100];
+		sprintf(txt, "BRIGHTNESS %d%%", brightness_percentage);
+		plat_status_msg_busy_first(txt);
+		/// ----- Shell cmd ----
+		sprintf(shell_cmd, "%s %d", SHELL_CMD_BRIGHTNESS_SET, brightness_percentage);
+		fp = popen(shell_cmd, "r");
+		if (fp == NULL) {
+			printf("Failed to run command %s\n", shell_cmd);
+		} else {
+	        	pclose(fp);
+		}
+	}
+	if (which & PEV_BRIGHT_DOWN)
+	{
+		printf("PEV_BRIGHT_DOWN\r\n");
+		/// ----- Compute new value -----
+		brightness_percentage = (brightness_percentage < STEP_CHANGE_BRIGHTNESS)?
+			0:(brightness_percentage-STEP_CHANGE_BRIGHTNESS);
+		/// ----- HUD msg ------
+		char txt[100];
+		sprintf(txt, "BRIGHTNESS %d%%", brightness_percentage);
+		plat_status_msg_busy_first(txt);
+		/// ----- Shell cmd ----
+		sprintf(shell_cmd, "%s %d", SHELL_CMD_BRIGHTNESS_SET, brightness_percentage);
+		fp = popen(shell_cmd, "r");
+		if (fp == NULL) {
+			printf("Failed to run command %s\n", shell_cmd);
+		} else {
+	        	pclose(fp);
+		}
+	}
+	if (which & PEV_AR_FACT_UP)
+	{
+		printf("PEV_AR_FACT_UP\r\n");
+		/// ----- Compute new value -----
+		if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){
+			aspect_ratio_factor_percent = (aspect_ratio_factor_percent+aspect_ratio_factor_step<100)?
+				aspect_ratio_factor_percent+aspect_ratio_factor_step:100;
+			need_screen_cleared = 1;
+		}
+		else{
+			aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
+		}
+		aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
+		/// ----- HUD msg ------
+		/*char txt[100];
+		sprintf(txt, "    DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent);
+		plat_status_msg_busy_first(txt);*/
+		sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: ZOOMED %d%%%%\"",
+			SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent);
+		fp = popen(shell_cmd, "r");
+		if (fp == NULL) {
+			printf("Failed to run command %s\n", shell_cmd);
+		} else {
+	        	pclose(fp);
+		}
+
+        // Save config file
+        configfile_save(cfg_file_rom);
+	}
+	if (which & PEV_AR_FACT_DOWN)
+	{
+		printf("PEV_AR_FACT_DOWN\r\n");
+		/// ----- Compute new value -----
+		if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){
+			aspect_ratio_factor_percent = (aspect_ratio_factor_percent>aspect_ratio_factor_step)?
+				aspect_ratio_factor_percent-aspect_ratio_factor_step:0;
+			need_screen_cleared = 1;
+		}
+		else{
+			aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
+		}
+		aspect_ratio = ASPECT_RATIOS_TYPE_MANUAL;
+		/// ----- HUD msg ------
+		/*char txt[100];
+		sprintf(txt, "    DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent);
+		plat_status_msg_busy_first(txt);*/
+		sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: ZOOMED %d%%%%\"",
+			SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent);
+		fp = popen(shell_cmd, "r");
+		if (fp == NULL) {
+			printf("Failed to run command %s\n", shell_cmd);
+		} else {
+	        	pclose(fp);
+		}
+
+        // Save config file
+        configfile_save(cfg_file_rom);
+
+	}
+	if (which & PEV_DISPMODE)
+	{
+		printf("PEV_DISPMODE\r\n");
+		/// ----- Compute new value -----
+		aspect_ratio = (aspect_ratio+1)%NB_ASPECT_RATIOS_TYPES;
+		/// ----- HUD msg ------
+		//char txt[100];
+		if(aspect_ratio == ASPECT_RATIOS_TYPE_MANUAL){
+			//sprintf(txt, "    DISPLAY MODE: ZOOMED - %d%%", aspect_ratio_factor_percent);
+			sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: ZOOMED %d%%%%\"",
+				SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_factor_percent);
+		}
+		else{
+			//sprintf(txt, "DISPLAY MODE: %s", aspect_ratio_name[aspect_ratio]);
+			sprintf(shell_cmd, "%s %d \"    DISPLAY MODE: %s\"",
+				SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_name[aspect_ratio]);
+		}
+		//plat_status_msg_busy_first(txt);
+		fp = popen(shell_cmd, "r");
+		if (fp == NULL) {
+			printf("Failed to run command %s\n", shell_cmd);
+		} else {
+	        	pclose(fp);
+		}
+
+        // Save config file
+        configfile_save(cfg_file_rom);
+	}
+	if (which & (PEV_SSLOT_PREV|PEV_SSLOT_NEXT))
+	{
+		if (which & PEV_SSLOT_PREV) {
+			state_slot -= 1;
+			if (state_slot < 0)
+				state_slot = 9;
+		} else {
+			state_slot += 1;
+			if (state_slot > 9)
+				state_slot = 0;
+		}
+
+		emu_status_msg("SAVE SLOT %i [%s]", state_slot,
+			emu_check_save_file(state_slot, NULL) ? "USED" : "FREE");
+	}
+	if (which & PEV_RESET)
+		emu_reset_game();
+	if (which & PEV_MENU)
+		engineState = PGS_Menu;
+}
+
+void emu_update_input(void)
+{
+	static int prev_events = 0;
+	int actions[IN_BINDTYPE_COUNT] = { 0, };
+	int pl_actions[2];
+	int events;
+
+	in_update(actions);
+
+	pl_actions[0] = actions[IN_BINDTYPE_PLAYER12];
+	pl_actions[1] = actions[IN_BINDTYPE_PLAYER12] >> 16;
+
+	PicoIn.pad[0] = pl_actions[0] & 0xfff;
+	PicoIn.pad[1] = pl_actions[1] & 0xfff;
+
+	if (pl_actions[0] & 0x7000)
+		do_turbo(&PicoIn.pad[0], pl_actions[0]);
+	if (pl_actions[1] & 0x7000)
+		do_turbo(&PicoIn.pad[1], pl_actions[1]);
+
+	events = actions[IN_BINDTYPE_EMU] & PEV_MASK;
+
+	// volume is treated in special way and triggered every frame
+	if (events & (PEV_VOL_DOWN|PEV_VOL_UP))
+		plat_update_volume(1, events & PEV_VOL_UP);
+
+	if ((events ^ prev_events) & PEV_FF) {
+		emu_set_fastforward(events & PEV_FF);
+		plat_update_volume(0, 0);
+		reset_timing = 1;
+	}
+
+	events &= ~prev_events;
+
+	/* SMS */
+	if (PicoIn.AHW == PAHW_PICO)
+		run_events_pico(events);
+
+	if (events)
+		run_events_ui(events);
+
+	if (movie_data)
+		update_movie();
+
+	prev_events = actions[IN_BINDTYPE_EMU] & PEV_MASK;
+}
+
+static void mkdir_path(char *path_with_reserve, int pos, const char *name)
+{
+	strcpy(path_with_reserve + pos, name);
+	if (plat_is_dir(path_with_reserve))
+		return;
+	if (mkdir(path_with_reserve, 0777) < 0)
+		lprintf("failed to create: %s\n", path_with_reserve);
+}
+
+void emu_cmn_forced_frame(int no_scale, int do_emu, void *buf)
+{
+	int po_old = PicoIn.opt;
+	int y;
+
+	for (y = 0; y < g_screen_height; y++)
+		memset32((short *)g_screen_ptr + g_screen_ppitch * y, 0,
+			 g_screen_width * 2 / 4);
+
+	PicoIn.opt &= ~POPT_ALT_RENDERER;
+	PicoIn.opt |= POPT_ACC_SPRITES;
+	if (!no_scale && currentConfig.scaling)
+		PicoIn.opt |= POPT_EN_SOFTSCALE;
+
+	PicoDrawSetOutFormat(PDF_RGB555, 1);
+	PicoDrawSetOutBuf(buf, g_screen_ppitch * 2);
+	Pico.m.dirtyPal = 1;
+	Pico.est.rendstatus |= PDRAW_DIRTY_SPRITES;
+	if (do_emu)
+		PicoFrame();
+	else
+		PicoFrameDrawOnly();
+
+	PicoIn.opt = po_old;
+}
+
+int emu_is_segaCD(){
+	return (PicoIn.AHW & PAHW_MCD);
+}
+
+/* Quick save and turn off the console */
+void quick_save_and_poweroff()
+{
+    FILE *fp;
+
+    printf("Save Instant Play file\n");
+
+    /* Send command to cancel any previously scheduled powerdown */
+    fp = popen(SHELL_CMD_POWERDOWN_HANDLE, "r");
+    if (fp == NULL)
+    {
+        /* Countdown is still ticking, so better do nothing
+	   than start writing and get interrupted!
+	*/
+        printf("Failed to cancel scheduled shutdown\n");
+	exit(0);
+    }
+    pclose(fp);
+
+    /* Save  */
+    emu_save_load_game_from_file(0, quick_save_file);
+
+    /* Perform Instant Play save and shutdown */
+    execlp(SHELL_CMD_INSTANT_PLAY, SHELL_CMD_INSTANT_PLAY,
+	"save", prog_name, "-loadStateFile", quick_save_file, mRomName, NULL);
+
+    /* Should not be reached */
+    printf("Failed to perform Instant Play save and shutdown\n");
+
+    /* Exit Emulator */
+    exit(0);
+}
+
+
+void emu_init(void)
+{
+	char path[512];
+	int pos;
+
+#if 0
+	// FIXME: handle through menu, etc
+	FILE *f;
+	f = fopen("32X_M_BIOS.BIN", "rb");
+	p32x_bios_m = malloc(2048);
+	fread(p32x_bios_m, 1, 2048, f);
+	fclose(f);
+	f = fopen("32X_S_BIOS.BIN", "rb");
+	p32x_bios_s = malloc(1024);
+	fread(p32x_bios_s, 1, 1024, f);
+	fclose(f);
+#endif
+
+	/* make dirs for saves */
+	pos = plat_get_root_dir(path, sizeof(path) - 4);
+	mkdir_path(path, pos, "mds");
+	mkdir_path(path, pos, "srm");
+	mkdir_path(path, pos, "brm");
+	mkdir_path(path, pos, "cfg");
+
+	pprof_init();
+
+	make_config_cfg(path);
+	config_readlrom(path);
+
+	PicoInit();
+	PicoIn.osdMessage = plat_status_msg_busy_next;
+	PicoIn.mcdTrayOpen = emu_tray_open;
+	PicoIn.mcdTrayClose = emu_tray_close;
+
+	sndout_init();
+}
+
+void emu_finish(void)
+{
+	// save SRAM
+	if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && Pico.sv.changed) {
+		emu_save_load_game(0, 1);
+		Pico.sv.changed = 0;
+	}
+
+	if (!(currentConfig.EmuOpt & EOPT_NO_AUTOSVCFG)) {
+		char cfg[512];
+		make_config_cfg(cfg);
+		config_writelrom(cfg);
+#ifdef __GP2X__
+		sync();
+#endif
+	}
+
+	pprof_finish();
+
+	PicoExit();
+	sndout_exit();
+}
+
+static void snd_write_nonblocking(int len)
+{
+	sndout_write_nb(PicoIn.sndOut, len);
+}
+
+void emu_sound_start(void)
+{
+	PicoIn.sndOut = NULL;
+
+	if (currentConfig.EmuOpt & EOPT_EN_SOUND)
+	{
+		int is_stereo = (PicoIn.opt & POPT_EN_STEREO) ? 1 : 0;
+		/// Hard Bypass Stereo to mono
+		//is_stereo = 0;
+
+		PsndRerate(Pico.m.frame_count ? 1 : 0);
+
+		printf("starting audio: %i len: %i stereo: %i, pal: %i\n",
+			PicoIn.sndRate, Pico.snd.len, is_stereo, Pico.m.pal);
+		sndout_start(PicoIn.sndRate, is_stereo);
+		PicoIn.writeSound = snd_write_nonblocking;
+		plat_update_volume(0, 0);
+		memset(sndBuffer, 0, sizeof(sndBuffer));
+		PicoIn.sndOut = sndBuffer;
+	}
+}
+
+void emu_sound_stop(void)
+{
+	sndout_stop();
+}
+
+void emu_sound_wait(void)
+{
+	sndout_wait();
+}
+
+static void emu_loop_prep(void)
+{
+	static int pal_old = -1;
+	static int filter_old = -1;
+
+	if (currentConfig.CPUclock != plat_target_cpu_clock_get())
+		plat_target_cpu_clock_set(currentConfig.CPUclock);
+
+	if (Pico.m.pal != pal_old) {
+		plat_target_lcdrate_set(Pico.m.pal);
+		pal_old = Pico.m.pal;
+	}
+
+	if (currentConfig.filter != filter_old) {
+		plat_target_hwfilter_set(currentConfig.filter);
+		filter_old = currentConfig.filter;
+	}
+
+	plat_target_gamma_set(currentConfig.gamma, 0);
+
+	pemu_loop_prep();
+}
+
+/* our tick here is 1 us right now */
+#define ms_to_ticks(x) (unsigned int)(x * 1000)
+#define get_ticks() plat_get_ticks_us()
+
+void emu_loop(void)
+{
+	int frames_done, frames_shown;	/* actual frames for fps counter */
+	int frame_nb = 0;
+	int target_frametime_x3;
+	unsigned int timestamp_x3 = 0;
+	unsigned int timestamp_aim_x3 = 0;
+	unsigned int timestamp_fps_x3 = 0;
+	char *notice_msg = NULL;
+	char fpsbuff[24];
+	int fskip_cnt = 0;
+
+	fpsbuff[0] = 0;
+
+	PicoLoopPrepare();
+
+	plat_video_loop_prepare();
+	emu_loop_prep();
+	pemu_sound_start();
+
+	/* number of ticks per frame */
+	if (Pico.m.pal)
+		target_frametime_x3 = 3 * ms_to_ticks(1000) / 50;
+	else
+		target_frametime_x3 = 3 * ms_to_ticks(1000) / 60;
+
+	reset_timing = 1;
+	frames_done = frames_shown = 0;
+
+	/* loop with resync every 1 sec. */
+	while (engineState == PGS_Running)
+	{
+		int skip = 0;
+		int diff;
+
+		pprof_start(main);
+
+		if (reset_timing) {
+			reset_timing = 0;
+			plat_video_wait_vsync();
+			timestamp_aim_x3 = get_ticks() * 3;
+			timestamp_fps_x3 = timestamp_aim_x3;
+			fskip_cnt = 0;
+		}
+		else if (currentConfig.EmuOpt & EOPT_NO_FRMLIMIT) {
+			timestamp_aim_x3 = get_ticks() * 3;
+		}
+
+		timestamp_x3 = get_ticks() * 3;
+
+		// show notice_msg message?
+		if (notice_msg_time != 0)
+		{
+			static int noticeMsgSum;
+			if (timestamp_x3 - ms_to_ticks(notice_msg_time) * 3
+			     > ms_to_ticks(STATUS_MSG_TIMEOUT) * 3)
+			{
+				notice_msg_time = 0;
+				notice_msg = NULL;
+				plat_status_msg_clear();
+			}
+			else {
+				int sum = noticeMsg[0] + noticeMsg[1] + noticeMsg[2];
+				if (sum != noticeMsgSum) {
+					plat_status_msg_clear();
+					noticeMsgSum = sum;
+				}
+				notice_msg = noticeMsg;
+			}
+		}
+
+		// second changed?
+		if (timestamp_x3 - timestamp_fps_x3 >= ms_to_ticks(1000) * 3)
+		{
+
+#define FUNKEY_RESYNCHRONIZE_AUDIO_SECS	(5*60)
+#ifdef FUNKEY_RESYNCHRONIZE_AUDIO_SECS
+			static unsigned int last_resync = 0, cur_sec=0;
+			if(cur_sec++ - last_resync >= FUNKEY_RESYNCHRONIZE_AUDIO_SECS){
+				last_resync = cur_sec;
+
+				/** Resync here (same as quitting ans relaunching loop for now) */
+				printf("Resync sound now\n");
+				emu_sound_stop();
+				pemu_sound_start();
+			}
+#endif //FUNKEY_RESYNCHRONIZE_AUDIO_SECS
+
+#ifdef BENCHMARK
+			static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];
+			if (++bench == 10) {
+				bench = 0;
+				bench_fps_s = bench_fps;
+				bf[bfp++ & 3] = bench_fps;
+				bench_fps = 0;
+			}
+			bench_fps += frames_shown;
+			sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);
+			printf("%s\n", fpsbuff);
+#else
+			if (currentConfig.EmuOpt & EOPT_SHOW_FPS || show_fps_bypass){
+				printf("%02i/%02i  \n", frames_shown, frames_done);
+				snprintf(fpsbuff, 8, "%02i/%02i  ", frames_shown, frames_done);
+			}
+#endif
+			frames_shown = frames_done = 0;
+			timestamp_fps_x3 += ms_to_ticks(1000) * 3;
+		}
+#ifdef PFRAMES
+		sprintf(fpsbuff, "%i", Pico.m.frame_count);
+#endif
+
+		diff = timestamp_aim_x3 - timestamp_x3;
+
+		if (currentConfig.Frameskip >= 0) // frameskip enabled (or 0)
+		{
+			if (fskip_cnt < currentConfig.Frameskip) {
+				fskip_cnt++;
+				skip = 1;
+			}
+			else {
+				fskip_cnt = 0;
+			}
+		}
+		else if (diff < -target_frametime_x3)
+		{
+			/* no time left for this frame - skip */
+			/* limit auto frameskip to max_skip */
+			if (fskip_cnt < currentConfig.max_skip) {
+				fskip_cnt++;
+				skip = 1;
+			}
+			else {
+				fskip_cnt = 0;
+			}
+		} else
+			fskip_cnt = 0;
+
+		// don't go in debt too much
+		while (diff < -target_frametime_x3 * 3) {
+			timestamp_aim_x3 += target_frametime_x3;
+			diff = timestamp_aim_x3 - timestamp_x3;
+		}
+
+        /* Quick save and poweroff */
+        if(mQuickSaveAndPoweroff){
+			quick_save_and_poweroff();
+			mQuickSaveAndPoweroff = 0;
+        }
+
+		emu_update_input();
+		if (skip) {
+			int do_audio = diff > -target_frametime_x3 * 2;
+			PicoIn.skipFrame = do_audio ? 1 : 2;
+			PicoFrame();
+			PicoIn.skipFrame = 0;
+		}
+		else {
+			PicoFrame();
+			pemu_finalize_frame(fpsbuff, notice_msg);
+			frames_shown++;
+		}
+		frames_done++;
+		timestamp_aim_x3 += target_frametime_x3;
+
+		if (!skip && !flip_after_sync)
+			plat_video_flip();
+
+		/* frame limiter */
+		if (!skip && !reset_timing
+		    && !(currentConfig.EmuOpt & (EOPT_NO_FRMLIMIT|EOPT_EXT_FRMLIMIT)))
+		{
+			unsigned int timestamp = get_ticks();
+			diff = timestamp_aim_x3 - timestamp * 3;
+
+			// sleep or vsync if we are still too fast
+			if (diff > target_frametime_x3 && (currentConfig.EmuOpt & EOPT_VSYNC)) {
+				// we are too fast
+				plat_video_wait_vsync();
+				timestamp = get_ticks();
+				diff = timestamp * 3 - timestamp_aim_x3;
+			}
+			if (diff > target_frametime_x3) {
+				// still too fast
+				plat_wait_till_us(timestamp + (diff - target_frametime_x3) / 3);
+			}
+		}
+
+		if (!skip && flip_after_sync)
+			plat_video_flip();
+
+
+		/* FOR SEGA CD */
+		/* Leave some time for the bios to load before starting a quick load at boot */
+		/* (Sega CD doesn't quick load at boot otherwise) */
+		/* Should have a much cleaner implementation thant this fix */
+		if((PicoIn.AHW & PAHW_MCD) && frame_nb >= 120){
+
+			/* Load slot */
+			if(load_state_slot != -1){
+				printf("LOADING FROM SLOT (SEGA CD) %d...\n", load_state_slot+1);
+				char fname[1024];
+				emu_save_load_game(1, 0);
+				printf("LOADED FROM SLOT (SEGA CD) %d\n", load_state_slot+1);
+				load_state_slot = -1;
+			}
+			if(need_quick_load != -1){
+				load_state_file = quick_save_file;
+				need_quick_load = -1;
+			}
+			/* Load file */
+			if(load_state_file != NULL){
+				printf("LOADING FROM FILE (SEGA CD) %s...\n", load_state_file);
+				emu_save_load_game_from_file(1, load_state_file);
+				printf("LOADED FROM SLOT (SEGA CD) %s\n", load_state_file);
+				load_state_file = NULL;
+			}
+		}
+
+		frame_nb++;
+
+		pprof_end(main);
+	}
+
+	emu_set_fastforward(0);
+
+	// save SRAM
+	if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && Pico.sv.changed) {
+		plat_status_msg_busy_first("Writing SRAM/BRAM...");
+		emu_save_load_game(0, 1);
+		Pico.sv.changed = 0;
+	}
+
+	pemu_loop_end();
+	emu_sound_stop();
+}

+ 27 - 2
platform/common/host_dasm.c

@@ -7,10 +7,10 @@
 #include <stdint.h>
 #include <string.h>
 
-#ifdef __mips__
+#if defined __mips__
 #include "dismips.c"
 #define disasm dismips
-#else
+#elif defined __arm__
 #include "disarm.c"
 #define disasm disarm
 #endif
@@ -36,6 +36,7 @@ static const char *lookup_name(void *addr)
   return NULL;
 }
 
+#ifdef disasm
 void host_dasm(void *addr, int len)
 {
   void *end = (char *)addr + len;
@@ -65,6 +66,30 @@ void host_dasm(void *addr, int len)
     addr = (char *)addr + sizeof(long);
   }
 }
+#else
+void host_dasm(void *addr, int len)
+{
+  uint8_t *end = (uint8_t *)addr + len;
+  char buf[64];
+  uint8_t *p = addr;
+  int i = 0, o = 0;
+
+  o = snprintf(buf, sizeof(buf), "%p:	", p);
+  while (p < end) {
+    o += snprintf(buf+o, sizeof(buf)-o, "%02x ", *p++);
+    if (++i >= 16) {
+      buf[o] = '\0';
+      printf("%s\n", buf);
+      o = snprintf(buf, sizeof(buf), "%p:	", p);
+      i = 0;
+    }
+  }
+  if (i) {
+    buf[o] = '\0';
+    printf("%s\n", buf);
+  }
+}
+#endif
 
 void host_dasm_new_symbol_(void *addr, const char *name)
 {

+ 1 - 1
platform/common/minimp3

@@ -1 +1 @@
-Subproject commit ef9e212fa29bb72d23558da21bb5694fd2d01768
+Subproject commit b18d274b998cd4406070ddc1f370f53392241af0

+ 65 - 0
platform/common/mp3_drmp3.c

@@ -0,0 +1,65 @@
+/*
+ * MP3 decoding using dr_mp3
+ * (C) kub, 2020
+ *
+ * This work is licensed under the terms of MAME license.
+ * See COPYING file in the top-level directory.
+ */
+
+#include <stdio.h>
+
+#include <pico/pico_int.h>
+#define DR_MP3_IMPLEMENTATION
+#include "dr_libs/dr_mp3.h"
+#include "mp3.h"
+
+static drmp3dec mp3dec;
+static unsigned char mp3_input_buffer[2 * 1024];
+
+int mp3dec_start(FILE *f, int fpos_start)
+{
+	drmp3dec_init(&mp3dec);
+	return 0;
+}
+
+int mp3dec_decode(FILE *f, int *file_pos, int file_len)
+{
+	drmp3dec_frame_info info;
+	unsigned char *readPtr;
+	int bytesLeft;
+	int offset; // mp3 frame offset from readPtr
+	int len;
+	int retry = 3;
+
+	do
+	{
+		if (*file_pos >= file_len)
+			return 1; /* EOF, nothing to do */
+
+		fseek(f, *file_pos, SEEK_SET);
+		bytesLeft = fread(mp3_input_buffer, 1, sizeof(mp3_input_buffer), f);
+
+		offset = mp3_find_sync_word(mp3_input_buffer, bytesLeft);
+		if (offset < 0) {
+			lprintf("find_sync_word (%i/%i) err %i\n",
+				*file_pos, file_len, offset);
+			*file_pos = file_len;
+			return 1; // EOF
+		}
+		*file_pos += offset;
+		readPtr = mp3_input_buffer + offset;
+		bytesLeft -= offset;
+
+		len = drmp3dec_decode_frame(&mp3dec, readPtr, bytesLeft, cdda_out_buffer, &info);
+		if (len > 0)			// retrieved decoded data
+			*file_pos += info.frame_bytes;
+		else if (info.frame_bytes > 0)	// no output but input consumed?
+			*file_pos += 1;			// try to skip frame
+		else if (offset == 0)		// bad frame?
+			*file_pos += 1;			// try resyncing
+		// else				// truncated frame, try more data
+	}
+	while (len <= 0 && --retry > 0);
+
+	return len <= 0;
+}

+ 1 - 1
platform/common/plat_sdl.c

@@ -1726,7 +1726,7 @@ void plat_video_loop_prepare(void)
 	// take over any new vout settings XXX ask plat_sdl for scaling instead!
 	plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, 0);
 	// switch over to scaled output if available
-	if (plat_sdl_overlay != NULL || plat_sdl_gl_active) {
+	if (plat_sdl_overlay != NULL || plat_sdl_gl_active || currentConfig.scaling != EOPT_SCALE_NONE) {
 		g_screen_width = 320;
 		g_screen_height = 240;
 		g_screen_ppitch = g_screen_width;

+ 21 - 5
platform/libretro/libretro.c

@@ -353,7 +353,7 @@ static void munmap(void *addr, size_t length)
 
 void* mmap(void *desired_addr, size_t len, int mmap_prot, int mmap_flags, int fildes, size_t off)
 {
-   return malloc(len);
+   return calloc(1, len);
 }
 
 void munmap(void *base_addr, size_t len)
@@ -706,6 +706,8 @@ void lprintf(const char *fmt, ...)
 }
 
 /* libretro */
+bool libretro_supports_bitmasks = false;
+
 void retro_set_environment(retro_environment_t cb)
 {
 #ifdef USE_LIBRETRO_VFS
@@ -1638,6 +1640,7 @@ void retro_run(void)
    bool updated = false;
    int pad, i;
    static void *buff;
+   int16_t input;
 
    PicoIn.skipFrame = 0;
 
@@ -1647,10 +1650,18 @@ void retro_run(void)
    input_poll_cb();
 
    PicoIn.pad[0] = PicoIn.pad[1] = 0;
-   for (pad = 0; pad < 2; pad++)
-      for (i = 0; i < RETRO_PICO_MAP_LEN; i++)
-         if (input_state_cb(pad, RETRO_DEVICE_JOYPAD, 0, i))
-            PicoIn.pad[pad] |= retro_pico_map[i];
+   for (pad = 0; pad < 2; pad++) {
+      if (libretro_supports_bitmasks) {
+         input = input_state_cb(pad, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
+         for (i = 0; i < RETRO_PICO_MAP_LEN; i++)
+            if (input & (1 << i))
+               PicoIn.pad[pad] |= retro_pico_map[i];
+      } else {
+         for (i = 0; i < RETRO_PICO_MAP_LEN; i++)
+            if (input_state_cb(pad, RETRO_DEVICE_JOYPAD, 0, i))
+               PicoIn.pad[pad] |= retro_pico_map[i];
+      }
+   }
 
    if (PicoPatches)
       PicoPatchApply();
@@ -1785,6 +1796,9 @@ void retro_init(void)
 
    environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
 
+   if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
+      libretro_supports_bitmasks = true;
+
 #ifdef _3DS
    ctr_svchack_successful = ctr_svchack_init();
    check_rosalina();
@@ -1853,4 +1867,6 @@ void retro_deinit(void)
 #endif
    vout_buf = NULL;
    PicoExit();
+
+   libretro_supports_bitmasks = false;
 }

+ 1 - 1
platform/linux/emu.c

@@ -32,7 +32,7 @@ void pemu_prep_defconfig(void)
 
 void pemu_validate_config(void)
 {
-#if !defined(__arm__) && !defined(__aarch64__) && !defined(__mips__) && !defined(__riscv__) &&  !defined(__riscv) && !defined(__powerpc__) && !defined(__i386__) && !defined(__x86_64__)
+#if !defined(__arm__) && !defined(__aarch64__) && !defined(__mips__) && !defined(__riscv__) &&  !defined(__riscv) && !defined(__powerpc__) && !defined(__ppc__) && !defined(__i386__) && !defined(__x86_64__)
 	PicoIn.opt &= ~POPT_EN_DRC;
 #endif
 }

+ 16 - 0
platform/linux/menu.c

@@ -0,0 +1,16 @@
+#include "../libpicofe/gp2x/plat_gp2x.h"
+
+// ------------ gfx options menu ------------
+
+
+const char *men_scaling_opts[] = { "OFF", "ON", NULL };
+
+#define MENU_OPTIONS_GFX \
+	// mee_enum      ("screen scaling",           MA_OPT_SCALING,        currentConfig.scaling, men_scaling_opts), \
+
+#define MENU_OPTIONS_ADV
+
+void linux_menu_init(void)
+{
+}
+

+ 1 - 1
platform/opendingux/data/default.gcw0.desktop

@@ -6,4 +6,4 @@ Exec=PicoDrive %f
 Icon=megadrive
 Terminal=false
 Categories=emulators;
-MimeType=.md;.smd;.bin;.sms;.cue;.32x;.zip;.7z
+MimeType=.md;.smd;.bin;.sms;.cue;.chd;.32x;.zip;.7z

+ 2 - 2
platform/opendingux/data/default.retrofw.desktop

@@ -3,10 +3,10 @@ Name=Picodrive
 Comment=A megadrive/genesis emulator
 Exec=PicoDrive %f
 Icon=megadrive
-Categories=games;
+Categories=emulators;
 X-OD-Manual=
 X-OD-Selector=
-X-OD-Filter=.md,.smd,.bin,.sms,.cue,.32x,.zip,.7z
+X-OD-Filter=.md,.smd,.bin,.sms,.cue,.chd,.32x,.zip,.7z
 Terminal=true
 Type=Application
 StartupNotify=true

+ 7 - 7
tools/release.sh

@@ -15,7 +15,7 @@
 #	rg350:		rg350-toolchain (+ mips-toolchain for newer gcc)
 #	psp:		pspdev
 
-mkdir -p release
+mkdir -p release-$1
 trap "exit" ERR
 
 # GPH devices: gp2x, wiz, caanoo, with ubuntu arm gcc 4.7
@@ -23,18 +23,18 @@ trap "exit" ERR
 TC=$HOME/opt/open2x/gcc-4.1.1-glibc-2.3.6 CROSS_COMPILE=arm-linux-gnueabi- CFLAGS="-I$TC/arm-open2x-linux/include -I$HOME/src/gp2x/armroot/include -U_FORTIFY_SOURCE" LDFLAGS="-B$TC/lib/gcc/arm-open2x-linux/4.1.1 -B$TC/arm-open2x-linux/lib -L$TC/arm-open2x-linux/lib -L$HOME/src/gp2x/armroot/lib" ./configure --platform=gp2x
 make clean all
 make -C platform/gp2x rel VER=$1
-mv PicoDrive_$1.zip release/PicoDrive-gph_$1.zip
+mv PicoDrive_$1.zip release-$1/PicoDrive-gph_$1.zip
 
 # dingux: dingoo a320, ritmix rzx-50, JZ4755 or older (mips32r1 w/o fpu)
 # NB works for legacy dingux and possibly opendingux before gcw0
 TC=$HOME/opt/opendingux-toolchain PATH=$TC/usr/bin:$PATH CROSS_COMPILE=mipsel-linux- CFLAGS="-I $TC/usr/include -I $TC/usr/include/SDL -Wno-unused-result -mabicalls" LDFLAGS="--sysroot $TC" ./configure --platform=dingux
 TC=$HOME/opt/opendingux-toolchain PATH=$TC/usr/bin:$PATH make clean all
-mv PicoDrive-dge.zip release/PicoDrive-dge_$1.zip
+mv PicoDrive-dge.zip release-$1/PicoDrive-dge_$1.zip
 
 # retrofw: rs-97 and similar, JZ4760 (mips32r1 with fpu)
 TC=$HOME/opt/mipsel-linux-uclibc PATH=$TC/bin:$PATH CROSS_COMPILE=mipsel-linux- CFLAGS="-I $TC/include -I $TC/include/SDL -Wno-unused-result" LDFLAGS="--sysroot $TC/mipsel-buildroot-linux-uclibc/sysroot" ./configure --platform=retrofw
 TC=$HOME/opt/mipsel-linux-uclibc PATH=$TC/bin:$PATH make clean all
-mv PicoDrive.opk release/PicoDrive-retrofw_$1.opk
+mv PicoDrive.opk release-$1/PicoDrive-retrofw_$1.opk
 
 # gcw0: JZ4770 (mips32r2 with fpu), swapped X/Y buttons, SDK toolchain
 #TC=$HOME/opt/gcw0-toolchain PATH=$TC/usr/bin:$PATH CROSS_COMPILE=mipsel-linux- TS=$TC/usr/mipsel-gcw0-linux-uclibc/sysroot CFLAGS="-I$TS/usr/include -I$TS/usr/include/SDL" LDFLAGS="--sysroot $TS" ./configure --platform=gcw0
@@ -43,7 +43,7 @@ mv PicoDrive.opk release/PicoDrive-retrofw_$1.opk
 # gcw0: JZ4770 (mips32r2 with fpu), swapped X/Y buttons, newer generic gcc
 TC=$HOME/opt/gcw0-toolchain PATH=$HOME/opt/mips-toolchain/bin:$PATH CROSS_COMPILE=mipsel-linux- TS=$TC/usr/mipsel-gcw0-linux-uclibc/sysroot CFLAGS="-I$TS/usr/include -I$TS/usr/include/SDL -mabicalls" LDFLAGS="--sysroot $TS -Wl,--dynamic-linker=/lib/ld-uClibc.so.0" ./configure --platform=gcw0
 PATH=$HOME/opt/mips-toolchain/usr/bin:$PATH make clean all
-mv PicoDrive.opk release/PicoDrive-gcw0_$1.opk
+mv PicoDrive.opk release-$1/PicoDrive-gcw0_$1.opk
 
 # rg350: JZ4770, SDK toolchain
 #TC=$HOME/opt/rg350-toolchain PATH=$TC/usr/bin:$PATH CROSS_COMPILE=mipsel-linux- TS=$TC/usr/mipsel-gcw0-linux-uclibc/sysroot CFLAGS="-I$TS/usr/include -I$TS/usr/include/SDL" LDFLAGS="--sysroot $TS" ./configure --platform=rg350
@@ -53,10 +53,10 @@ mv PicoDrive.opk release/PicoDrive-gcw0_$1.opk
 # NB this may or may not work on the newer opendingux beta
 TC=$HOME/opt/rg350-toolchain PATH=$HOME/opt/mips-toolchain/bin:$PATH CROSS_COMPILE=mipsel-linux- TS=$TC/usr/mipsel-gcw0-linux-uclibc/sysroot CFLAGS="-I$TS/usr/include -I$TS/usr/include/SDL -mabicalls" LDFLAGS="--sysroot $TS -Wl,--dynamic-linker=/lib/ld-uClibc.so.0" ./configure --platform=opendingux
 PATH=$HOME/opt/mips-toolchain/usr/bin:$PATH make clean all
-mv PicoDrive.opk release/PicoDrive-opendingux_$1.opk
+mv PicoDrive.opk release-$1/PicoDrive-opendingux_$1.opk
 
 # psp (experimental), pspdev SDK toolchain
 TC=$HOME/opt/pspdev PATH=$TC/bin:$PATH CROSS_COMPILE=psp- CFLAGS="-I$TC/psp/sdk/include -D_POSIX_C_SOURCE=199506L" LDFLAGS="-L$TC/psp/sdk/lib" ./configure --platform=psp
 TC=$HOME/opt/pspdev PATH=$TC/bin:$PATH make clean all
 make -C platform/psp rel VER=$1
-mv PicoDrive_psp_$1.zip release/PicoDrive-psp_$1.zip
+mv PicoDrive_psp_$1.zip release-$1/PicoDrive-psp_$1.zip