diff --git a/.gitmodules b/.gitmodules index f38d3399..026e51cb 100644 --- a/.gitmodules +++ b/.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 diff --git a/Makefile b/Makefile index 93e4e8de..ae9d5abd 100644 --- a/Makefile +++ b/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 diff --git a/Makefile.libretro b/Makefile.libretro index 6a189970..9effd3c8 100644 --- a/Makefile.libretro +++ b/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) diff --git a/cpu/cyclone b/cpu/cyclone index 94a9d9a8..1f03ed6a 160000 --- a/cpu/cyclone +++ b/cpu/cyclone @@ -1 +1 @@ -Subproject commit 94a9d9a888e72cbfa4db12113cd113cf5e154f7f +Subproject commit 1f03ed6a49252bfb900c4d3932094308e1c5b375 diff --git a/cpu/drc/emit_arm64.c b/cpu/drc/emit_arm64.c index 66452975..089cfdb2 100644 --- a/cpu/drc/emit_arm64.c +++ b/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) /**/ diff --git a/cpu/drc/emit_ppc.c b/cpu/drc/emit_ppc.c index e6c43cde..0b4f0649 100644 --- a/cpu/drc/emit_ppc.c +++ b/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) diff --git a/cpu/drc/emit_x86.c b/cpu/drc/emit_x86.c index 47124498..94111e0f 100644 --- a/cpu/drc/emit_x86.c +++ b/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 { \ diff --git a/cpu/fame/famec_opcodes.h b/cpu/fame/famec_opcodes.h index e069e9ea..ffb7c4ed 100644 --- a/cpu/fame/famec_opcodes.h +++ b/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 diff --git a/cpu/musashi/m68k_in.c b/cpu/musashi/m68k_in.c index d1756a53..14c1347d 100644 --- a/cpu/musashi/m68k_in.c +++ b/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 diff --git a/cpu/musashi/m68kcpu.c b/cpu/musashi/m68kcpu.c index 38469f32..d729f2cf 100644 --- a/cpu/musashi/m68kcpu.c +++ b/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(); } diff --git a/cpu/musashi/m68kcpu.h b/cpu/musashi/m68kcpu.h index a1dff7de..6ab0ba0b 100644 --- a/cpu/musashi/m68kcpu.h +++ b/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 +typedef uint64_t UINT64; +typedef uint32_t UINT32; +typedef uint16_t UINT16; +typedef uint8_t UINT8; #include "m68k.h" #include @@ -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 diff --git a/cpu/musashi/m68kmake.c b/cpu/musashi/m68kmake.c index a544a5c2..6dd57fca 100644 --- a/cpu/musashi/m68kmake.c +++ b/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) diff --git a/cpu/sh2/compiler.c b/cpu/sh2/compiler.c index 586175de..08b2c80f 100644 --- a/cpu/sh2/compiler.c +++ b/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 diff --git a/cpu/sh2/compiler.h b/cpu/sh2/compiler.h index dda3b91d..f0322743 100644 --- a/cpu/sh2/compiler.h +++ b/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 diff --git a/cpu/sh2/mame/sh2pico.c b/cpu/sh2/mame/sh2pico.c index 380ca24f..f4ae85cb 100644 --- a/cpu/sh2/mame/sh2pico.c +++ b/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; } diff --git a/cpu/sh2/sh2.h b/cpu/sh2/sh2.h index c3ca1146..614e7de1 100644 --- a/cpu/sh2/sh2.h +++ b/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; diff --git a/pico/32x/32x.c b/pico/32x/32x.c index bf3c9ec0..a34e8075 100644 --- a/pico/32x/32x.c +++ b/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); } } diff --git a/pico/32x/draw.c b/pico/32x/draw.c index f5638b96..cc98a692 100644 --- a/pico/32x/draw.c +++ b/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)) diff --git a/pico/32x/draw_arm.S b/pico/32x/draw_arm.S index bf26f746..cdcb211a 100644 --- a/pico/32x/draw_arm.S +++ b/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 diff --git a/pico/32x/memory.c b/pico/32x/memory.c index fe9ae57d..5bac31b8 100644 --- a/pico/32x/memory.c +++ b/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 diff --git a/pico/32x/sh2soc.c b/pico/32x/sh2soc.c index ae9474d2..f8bb4e79 100644 --- a/pico/32x/sh2soc.c +++ b/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); } diff --git a/pico/cart.c b/pico/cart.c index 81fc7fb0..1d17c713 100644 --- a/pico/cart.c +++ b/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 diff --git a/pico/cd/cdc.c b/pico/cd/cdc.c index bf688f57..70914636 100644 --- a/pico/cd/cdc.c +++ b/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); } } diff --git a/pico/cd/cdd.c b/pico/cd/cdd.c index fd78c357..00e12bad 100644 --- a/pico/cd/cdd.c +++ b/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) { diff --git a/pico/cd/gfx.c b/pico/cd/gfx.c index b7d9cf1c..0a231b7a 100644 --- a/pico/cd/gfx.c +++ b/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 { diff --git a/pico/cd/libchdr b/pico/cd/libchdr index ff3175dd..00319cf3 160000 --- a/pico/cd/libchdr +++ b/pico/cd/libchdr @@ -1 +1 @@ -Subproject commit ff3175ddb040b1a967c665a28868b16bf5aa97dd +Subproject commit 00319cf31f034e4d468a49a60265c7c5b8305b70 diff --git a/pico/cd/mcd.c b/pico/cd/mcd.c index 559fca7a..732a4299 100644 --- a/pico/cd/mcd.c +++ b/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); diff --git a/pico/cd/memory.c b/pico/cd/memory.c index 7cf9ffd5..05ee3e13 100644 --- a/pico/cd/memory.c +++ b/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 diff --git a/pico/cd/pcm.c b/pico/cd/pcm.c index 27fb2ac9..795ec3b7 100644 --- a/pico/cd/pcm.c +++ b/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; diff --git a/pico/m68kif_cyclone.s b/pico/m68kif_cyclone.s index ec3087e5..6f2a9bcc 100644 --- a/pico/m68kif_cyclone.s +++ b/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} diff --git a/pico/misc.c b/pico/misc.c index edf82b3a..9065476b 100644 --- a/pico/misc.c +++ b/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) diff --git a/pico/patch.c b/pico/patch.c index 4b6b5472..b4c2d898 100644 --- a/pico/patch.c +++ b/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 { diff --git a/pico/pico.c b/pico/pico.c index 577701ba..b5ac2641 100644 --- a/pico/pico.c +++ b/pico/pico.c @@ -40,6 +40,7 @@ void PicoInit(void) Pico32xInit(); PsndInit(); + PicoVideoInit(); PicoDrawInit(); PicoDraw2Init(); } diff --git a/pico/pico_cmn.c b/pico/pico_cmn.c index c5d72278..d1ebe22b 100644 --- a/pico/pico_cmn.c +++ b/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); } } diff --git a/pico/pico_int.h b/pico/pico_int.h index cc3d2f44..18e33418 100644 --- a/pico/pico_int.h +++ b/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); diff --git a/pico/pico_port.h b/pico/pico_port.h index a5f8a582..d2b78e05 100644 --- a/pico/pico_port.h +++ b/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 diff --git a/pico/sek.c b/pico/sek.c index 6c340672..ccbddb8e 100644 --- a/pico/sek.c +++ b/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) diff --git a/pico/sound/mix_arm.S b/pico/sound/mix_arm.S index 60438988..9aa05b28 100644 --- a/pico/sound/mix_arm.S +++ b/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 diff --git a/pico/sound/sound.c b/pico/sound/sound.c index 806a597b..76a9be84 100644 --- a/pico/sound/sound.c +++ b/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; diff --git a/pico/sound/ym2612.c b/pico/sound/ym2612.c index 6b3e4bab..75d33de6 100644 --- a/pico/sound/ym2612.c +++ b/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++) { diff --git a/pico/videoport.c b/pico/videoport.c index 33548cb8..44a02fbb 100644 --- a/pico/videoport.c +++ b/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 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; } diff --git a/platform/common/config_file.c b/platform/common/config_file.c index 59bbe122..8c80f7d5 100644 --- a/platform/common/config_file.c +++ b/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; diff --git a/platform/common/dr_libs b/platform/common/dr_libs new file mode 160000 index 00000000..b20f7512 --- /dev/null +++ b/platform/common/dr_libs @@ -0,0 +1 @@ +Subproject commit b20f7512a69b732ab4082c05e01fc29d42333fae diff --git a/platform/common/emu.c b/platform/common/emu.c index e479e1e9..627374f6 100644 --- a/platform/common/emu.c +++ b/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 -#include -#include -#include -#ifdef __GP2X__ -#include -#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 -#include - -#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 /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; -} - -// -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(¤tConfig, &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 +#include +#include +#include +#ifdef __GP2X__ +#include +#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 +#include + +#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 /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; +} + +// +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(¤tConfig, &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(); +} diff --git a/platform/common/host_dasm.c b/platform/common/host_dasm.c index 03a28946..fb7a4259 100644 --- a/platform/common/host_dasm.c +++ b/platform/common/host_dasm.c @@ -7,10 +7,10 @@ #include #include -#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) { diff --git a/platform/common/minimp3 b/platform/common/minimp3 index ef9e212f..b18d274b 160000 --- a/platform/common/minimp3 +++ b/platform/common/minimp3 @@ -1 +1 @@ -Subproject commit ef9e212fa29bb72d23558da21bb5694fd2d01768 +Subproject commit b18d274b998cd4406070ddc1f370f53392241af0 diff --git a/platform/common/mp3_drmp3.c b/platform/common/mp3_drmp3.c new file mode 100644 index 00000000..54d3496d --- /dev/null +++ b/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 + +#include +#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; +} diff --git a/platform/common/plat_sdl.c b/platform/common/plat_sdl.c index 8d1ebf3e..03ec9823 100644 --- a/platform/common/plat_sdl.c +++ b/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; diff --git a/platform/libretro/libretro.c b/platform/libretro/libretro.c index 25ae8437..0969017b 100644 --- a/platform/libretro/libretro.c +++ b/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; } diff --git a/platform/linux/emu.c b/platform/linux/emu.c index 191af764..577243fa 100644 --- a/platform/linux/emu.c +++ b/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 } diff --git a/platform/linux/menu.c b/platform/linux/menu.c new file mode 100644 index 00000000..b9bf4cee --- /dev/null +++ b/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) +{ +} + diff --git a/platform/opendingux/data/default.gcw0.desktop b/platform/opendingux/data/default.gcw0.desktop index 3e17a75e..bfdb4298 100644 --- a/platform/opendingux/data/default.gcw0.desktop +++ b/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 diff --git a/platform/opendingux/data/default.retrofw.desktop b/platform/opendingux/data/default.retrofw.desktop index 84a35894..97c977bc 100644 --- a/platform/opendingux/data/default.retrofw.desktop +++ b/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 diff --git a/tools/release.sh b/tools/release.sh index 9388c710..a01904ea 100644 --- a/tools/release.sh +++ b/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