From ca482e5de8bacb70db55f43afe02f93fe6fe3f16 Mon Sep 17 00:00:00 2001 From: notaz Date: Thu, 21 Aug 2008 10:45:54 +0000 Subject: [PATCH] UIQ3 update, some makefile unification, rm old configs, stuff git-svn-id: file:///home/notaz/opt/svn/PicoDrive@569 be3aeb3a-fb24-0410-a615-afba39da0efa --- Pico/Draw.c | 2 +- Pico/Pico.c | 1 + Pico/cd/buffering.c | 2 +- Pico/cd/cue.c | 3 + platform/common/common_arm.mak | 66 +++ platform/common/config.c | 81 ++-- platform/common/emu.c | 185 ++++---- platform/common/emu.h | 39 +- platform/common/menu.c | 50 ++- platform/common/menu.h | 1 + platform/gizmondo/Makefile | 67 +-- platform/gizmondo/emu.c | 14 +- platform/gizmondo/main.c | 2 +- platform/gizmondo/menu.c | 12 +- platform/gizmondo/port_config.h | 6 + platform/gp2x/Makefile | 78 +--- platform/gp2x/emu.c | 15 +- platform/gp2x/main.c | 4 +- platform/gp2x/menu.c | 12 +- platform/gp2x/port_config.h | 6 + platform/pandora/emu.c | 15 +- platform/pandora/main.c | 4 +- platform/pandora/port_config.h | 6 + platform/psp/emu.c | 13 +- platform/psp/main.c | 4 +- platform/psp/menu.c | 12 +- platform/psp/port_config.h | 6 + platform/uiq3/App.cpp | 75 ++-- platform/uiq3/App.h | 19 +- platform/uiq3/Dialogs.cpp | 153 ++++--- platform/uiq3/Dialogs.h | 8 +- platform/uiq3/Engine.cpp | 232 +++------- platform/uiq3/Engine.h | 25 +- platform/uiq3/Makefile | 405 ++++++------------ platform/uiq3/Makefile.old | 307 +++++++++++++ platform/uiq3/_out/Makefile | 7 - platform/uiq3/_out/PicoDrive.pkg | 25 -- platform/uiq3/_out/PicoDrive.zip | Bin 157336 -> 0 bytes platform/uiq3/_out/PicoDrive_gcce.pkg | 25 -- platform/uiq3/{_out => }/config.txt | 0 platform/uiq3/emu.c | 138 ++++++ platform/uiq3/emu.h | 11 + platform/uiq3/engine/audio_mediaserver.cpp | 66 ++- platform/uiq3/engine/audio_mediaserver.h | 22 +- platform/uiq3/engine/blit.s | 11 + platform/uiq3/engine/debug.cpp | 61 +-- platform/uiq3/engine/debug.h | 2 +- platform/uiq3/engine/main.cpp | 298 +++++-------- platform/uiq3/engine/vid.cpp | 121 +++--- platform/uiq3/makezip.cmd | 3 - .../uiq3/{PicoDrive.hrh => picodrive.hrh} | 12 +- platform/uiq3/picodrive.pkg | 9 + platform/uiq3/port_config.h | 33 +- platform/uiq3/port_config.s | 15 +- platform/uiq3/qconn.cmd | 1 - platform/uiq3/qlog.cmd | 1 - platform/uiq3/qup.cmd | 1 - platform/uiq3/rsc/PicoDrive_loc.rss | 20 - .../uiq3/rsc/{PicoDrive.rss => picodrive.rss} | 78 +++- platform/uiq3/rsc/picodrive_loc.rss | 56 +++ .../{PicoDrive_reg.rss => picodrive_reg.rss} | 6 +- platform/uiq3/uiq3.mak | 167 ++++++++ platform/uiq3/version.h | 4 +- unzip/unzip.c | 4 - unzip/unzip_stream.c | 4 - zlib/zconf.h | 4 + 66 files changed, 1753 insertions(+), 1382 deletions(-) create mode 100644 platform/common/common_arm.mak create mode 100644 platform/uiq3/Makefile.old delete mode 100644 platform/uiq3/_out/Makefile delete mode 100644 platform/uiq3/_out/PicoDrive.pkg delete mode 100644 platform/uiq3/_out/PicoDrive.zip delete mode 100644 platform/uiq3/_out/PicoDrive_gcce.pkg rename platform/uiq3/{_out => }/config.txt (100%) create mode 100644 platform/uiq3/emu.c create mode 100644 platform/uiq3/emu.h delete mode 100644 platform/uiq3/makezip.cmd rename platform/uiq3/{PicoDrive.hrh => picodrive.hrh} (83%) create mode 100644 platform/uiq3/picodrive.pkg delete mode 100644 platform/uiq3/qconn.cmd delete mode 100644 platform/uiq3/qlog.cmd delete mode 100644 platform/uiq3/qup.cmd delete mode 100644 platform/uiq3/rsc/PicoDrive_loc.rss rename platform/uiq3/rsc/{PicoDrive.rss => picodrive.rss} (88%) create mode 100644 platform/uiq3/rsc/picodrive_loc.rss rename platform/uiq3/rsc/{PicoDrive_reg.rss => picodrive_reg.rss} (83%) create mode 100644 platform/uiq3/uiq3.mak diff --git a/Pico/Draw.c b/Pico/Draw.c index 6d64cd6..7dadb4b 100644 --- a/Pico/Draw.c +++ b/Pico/Draw.c @@ -1444,7 +1444,7 @@ void PicoDrawSync(int to, int blank_last_line) } #if !CAN_HANDLE_240_LINES - if (DrawScanline >= 224) DrawScanline = 240, return; + if (DrawScanline >= 224) { DrawScanline = 240; return; } #endif // last line diff --git a/Pico/Pico.c b/Pico/Pico.c index 6bf28b6..296d5d5 100644 --- a/Pico/Pico.c +++ b/Pico/Pico.c @@ -49,6 +49,7 @@ void PicoExit(void) { if (PicoAHW & PAHW_MCD) PicoExitMCD(); + PicoCartUnload(); z80_exit(); if (SRam.data) free(SRam.data); SRam.data=0; diff --git a/Pico/cd/buffering.c b/Pico/cd/buffering.c index 826184b..e0e888d 100644 --- a/Pico/cd/buffering.c +++ b/Pico/cd/buffering.c @@ -12,7 +12,7 @@ static int hits, reads; void PicoCDBufferInit(void) { - void *tmp; + void *tmp = NULL; prev_lba = 0x80000000; hits = reads = 0; diff --git a/Pico/cd/cue.c b/Pico/cd/cue.c index 84deba7..d02062c 100644 --- a/Pico/cd/cue.c +++ b/Pico/cd/cue.c @@ -9,6 +9,9 @@ #ifdef _MSC_VER #define snprintf _snprintf #endif +#ifdef UIQ3 +#define snprintf(b,s,...) sprintf(b,##__VA_ARGS__) +#endif static char *mystrip(char *str) { diff --git a/platform/common/common_arm.mak b/platform/common/common_arm.mak new file mode 100644 index 0000000..5308fb3 --- /dev/null +++ b/platform/common/common_arm.mak @@ -0,0 +1,66 @@ +.c.o: + @echo ">>>" $< + $(CC) $(CFLAGS) $(DEFINC) -c $< -o $@ + +.S.o: + @echo ">>>" $< + $(CC) $(SFLAGS) $(DEFINC) -c $< -o $@ + + +../../tools/textfilter: ../../tools/textfilter.c + make -C ../../tools/ textfilter + +clean_prof: + find ../.. -name '*.gcno' -delete + find ../.. -name '*.gcda' -delete + +mkdirs: + mkdir -p $(DIRS) + +# deps +Pico/carthw/svp/compiler.o : ../../Pico/carthw/svp/ssp16.o ../../Pico/carthw/svp/gen_arm.c +Pico/Pico.o Pico/cd/Pico.o : ../../Pico/PicoFrameHints.c ../../Pico/PicoInt.h +Pico/Memory.o Pico/cd/Memory.o : ../../Pico/MemoryCmn.c ../../Pico/PicoInt.h + +# individual rules +Pico/draw_asm.o : ../../Pico/Draw.s + @echo ">>>" $@ + $(AS) $(ASFLAGS) $< -o $@ +Pico/draw2_asm.o : ../../Pico/Draw2.s + @echo ">>>" $@ + $(AS) $(ASFLAGS) $< -o $@ +Pico/memory_asm.o : ../../Pico/Memory.s + @echo ">>>" $@ + $(AS) $(ASFLAGS) $< -o $@ +Pico/sound/ym2612_asm.o : ../../Pico/sound/ym2612.s + @echo ">>>" $@ + $(AS) $(ASFLAGS) $< -o $@ +Pico/sound/mix_asm.o : ../../Pico/sound/mix.s + @echo ">>>" $@ + $(AS) $(ASFLAGS) $< -o $@ +Pico/misc_asm.o : ../../Pico/Misc.s + @echo ">>>" $@ + $(AS) $(ASFLAGS) $< -o $@ +Pico/cd/pico_asm.o : ../../Pico/cd/Pico.s + @echo ">>>" $@ + $(AS) $(ASFLAGS) $< -o $@ +Pico/cd/memory_asm.o : ../../Pico/cd/Memory.s + @echo ">>>" $@ + $(AS) $(ASFLAGS) $< -o $@ +Pico/cd/misc_asm.o : ../../Pico/cd/Misc.s + @echo ">>>" $@ + $(AS) $(ASFLAGS) $< -o $@ +#Pico/carthw/svp/stub_arm.o : ../../Pico/carthw/svp/stub_arm.S +# @echo ">>>" $@ +# $(GCC) $(CFLAGS) $(DEFINC) -c $< -o $@ + +# build Cyclone +cpu/Cyclone/proj/Cyclone.s: + @echo building Cyclone... + @make -C ../../cpu/Cyclone/proj CONFIG_FILE=config_pico.h + +# build helix libs +../common/helix/$(CROSS)helix-mp3.a: + make -C ../common/helix clean all + + diff --git a/platform/common/config.c b/platform/common/config.c index eb6cedb..f8fe5d0 100644 --- a/platform/common/config.c +++ b/platform/common/config.c @@ -1,11 +1,14 @@ /* * Human-readable config file management for PicoDrive - * (c) + * (c) notaz, 2008 */ #include #include #include +#ifdef UIQ3 +#include +#endif #include "config.h" #include "lprintf.h" @@ -205,6 +208,7 @@ static void custom_write(FILE *f, const menu_entry *me, int no_def) } +#if PLAT_HAVE_JOY static const char *joyKeyNames[32] = { "UP", "DOWN", "LEFT", "RIGHT", "b1", "b2", "b3", "b4", @@ -212,31 +216,40 @@ static const char *joyKeyNames[32] = "b13", "b14", "b15", "b16", "b17", "b19", "b19", "b20", "b21", "b22", "b23", "b24", "b25", "b26", "b27", "b28" }; +#endif static void keys_write(FILE *fn, const char *bind_str, const int binds[32], - const int def_binds[32], const char * const names[32], int no_defaults) + const int def_binds[32], const char * const names[32], int key_count, int no_defaults) { int t, i; - char act[48]; + char act[48], name[32]; - for (t = 0; t < 32; t++) + for (t = 0; t < key_count; t++) { act[0] = act[31] = 0; if (no_defaults && binds[t] == def_binds[t]) continue; - if (strcmp(names[t], "???") == 0) continue; #ifdef __GP2X__ if (strcmp(names[t], "SELECT") == 0) continue; #endif + if (t >= 32 || names[t] == NULL || strcmp(names[t], "???") == 0) { + if ((t >= '0' && t <= '9') || (t >= 'a' && t <= 'z') || (t >= 'A' && t <= 'Z')) + sprintf(name, "%c", t); + else + sprintf(name, "\\x%02x", t); + } + else + strcpy(name, names[t]); + if (binds[t] == 0 && def_binds[t] != 0) { - fprintf(fn, "%s %s =" NL, bind_str, names[t]); // no binds + fprintf(fn, "%s %s =" NL, bind_str, name); // no binds continue; } for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) { if (me_ctrl_actions[i].mask & binds[t]) { strncpy(act, me_ctrl_actions[i].name, 31); - fprintf(fn, "%s %s = player%i %s" NL, bind_str, names[t], + fprintf(fn, "%s %s = player%i %s" NL, bind_str, name, ((binds[t]>>16)&1)+1, mystrip(act)); } } @@ -244,7 +257,7 @@ static void keys_write(FILE *fn, const char *bind_str, const int binds[32], for (i = 0; emuctrl_actions[i].name != NULL; i++) { if (emuctrl_actions[i].mask & binds[t]) { strncpy(act, emuctrl_actions[i].name, 31); - fprintf(fn, "%s %s = %s" NL, bind_str, names[t], mystrip(act)); + fprintf(fn, "%s %s = %s" NL, bind_str, name, mystrip(act)); } } } @@ -281,8 +294,9 @@ static int default_var(const menu_entry *me) case MA_CDOPT_LEDS: return defaultConfig.EmuOpt; - case MA_CTRL_TURBO_RATE: - return defaultConfig.turbo_rate; + case MA_CTRL_TURBO_RATE: return defaultConfig.turbo_rate; + case MA_OPT_SCALING: return defaultConfig.scaling; + case MA_OPT_ROTATION: return defaultConfig.rotation; case MA_OPT_SAVE_SLOT: default: @@ -384,11 +398,13 @@ write: } // save key config - keys_write(fn, "bind", currentConfig.KeyBinds, defaultConfig.KeyBinds, keyNames, no_defaults); - keys_write(fn, "bind_joy0", currentConfig.JoyBinds[0], defaultConfig.JoyBinds[0], joyKeyNames, 1); - keys_write(fn, "bind_joy1", currentConfig.JoyBinds[1], defaultConfig.JoyBinds[1], joyKeyNames, 1); - keys_write(fn, "bind_joy2", currentConfig.JoyBinds[2], defaultConfig.JoyBinds[2], joyKeyNames, 1); - keys_write(fn, "bind_joy3", currentConfig.JoyBinds[3], defaultConfig.JoyBinds[3], joyKeyNames, 1); + keys_write(fn, "bind", currentConfig.KeyBinds, defaultConfig.KeyBinds, keyNames, PLAT_MAX_KEYS, no_defaults); +#if PLAT_HAVE_JOY + keys_write(fn, "bind_joy0", currentConfig.JoyBinds[0], defaultConfig.JoyBinds[0], joyKeyNames, 32, 1); + keys_write(fn, "bind_joy1", currentConfig.JoyBinds[1], defaultConfig.JoyBinds[1], joyKeyNames, 32, 1); + keys_write(fn, "bind_joy2", currentConfig.JoyBinds[2], defaultConfig.JoyBinds[2], joyKeyNames, 32, 1); + keys_write(fn, "bind_joy3", currentConfig.JoyBinds[3], defaultConfig.JoyBinds[3], joyKeyNames, 32, 1); +#endif #ifndef PSP if (section == NULL) @@ -423,7 +439,7 @@ int config_writelrom(const char *fname) int size; FILE *f; - if (strlen(lastRomFile) == 0) return -1; + if (strlen(loadedRomFName) == 0) return -1; f = fopen(fname, "r"); if (f != NULL) @@ -456,7 +472,7 @@ int config_writelrom(const char *fname) fwrite(old_data, 1, optr - old_data, f); free(old_data); } - fprintf(f, "LastUsedROM = %s" NL, lastRomFile); + fprintf(f, "LastUsedROM = %s" NL, loadedRomFName); fclose(f); return 0; } @@ -487,9 +503,9 @@ int config_readlrom(const char *fname) tmp++; mystrip(tmp); - len = sizeof(lastRomFile); - strncpy(lastRomFile, tmp, len); - lastRomFile[len-1] = 0; + len = sizeof(loadedRomFName); + strncpy(loadedRomFName, tmp, len); + loadedRomFName[len-1] = 0; ret = 0; break; } @@ -686,16 +702,29 @@ static int custom_read(menu_entry *me, const char *var, const char *val) static unsigned int keys_encountered = 0; -static void keys_parse(const char *var, const char *val, int binds[32], const char * const names[32]) +static void keys_parse(const char *var, const char *val, int binds[32], + const char * const names[32], int max_keys) { int t, i; unsigned int player; for (t = 0; t < 32; t++) { - if (strcmp(names[t], var) == 0) break; + if (names[t] && strcmp(names[t], var) == 0) break; } - if (t == 32) { + if (t == 32) + { + int len = strlen(var); + if (len == 1) t = var[0]; + else if (len >= 4 && var[0] == '\\' && var[1] == 'x') { + char *p; + t = (int)strtoul(var + 2, &p, 16); + if (*p != 0) t = max_keys; // parse failed + } + else + t = max_keys; // invalid + } + if (t < 0 || t >= max_keys) { lprintf("unhandled bind \"%s\"\n", var); return; } @@ -732,7 +761,7 @@ fail: #define try_joy_parse(num) { \ if (strncasecmp(var, "bind_joy"#num " ", 10) == 0) { \ - keys_parse(var + 10, val, currentConfig.JoyBinds[num], joyKeyNames); \ + keys_parse(var + 10, val, currentConfig.JoyBinds[num], joyKeyNames, 32); \ return; \ } \ } @@ -752,13 +781,15 @@ static void parse(const char *var, const char *val) // key binds if (strncasecmp(var, "bind ", 5) == 0) { - keys_parse(var + 5, val, currentConfig.KeyBinds, keyNames); + keys_parse(var + 5, val, currentConfig.KeyBinds, keyNames, PLAT_MAX_KEYS); return; } +#if PLAT_HAVE_JOY try_joy_parse(0) try_joy_parse(1) try_joy_parse(2) try_joy_parse(3) +#endif for (t = 0; t < sizeof(cfg_opts) / sizeof(cfg_opts[0]) && ret == 0; t++) { diff --git a/platform/common/emu.c b/platform/common/emu.c index 494484b..7dbac50 100644 --- a/platform/common/emu.c +++ b/platform/common/emu.c @@ -26,10 +26,10 @@ char *PicoConfigFile = "config.cfg"; currentConfig_t currentConfig, defaultConfig; int rom_loaded = 0; -char noticeMsg[64]; +char noticeMsg[64] = { 0, }; int state_slot = 0; int config_slot = 0, config_slot_current = 0; -char lastRomFile[512]; +char loadedRomFName[512] = { 0, }; int kb_combo_keys = 0, kb_combo_acts = 0; // keys and actions which need button combos int pico_inp_mode = 0; @@ -37,10 +37,8 @@ unsigned char *movie_data = NULL; static int movie_size = 0; // provided by platform code: -extern char romFileName[]; extern void emu_noticeMsgUpdated(void); -extern void emu_getMainDir(char *dst, int len); -extern void emu_setDefaultConfig(void); +extern int emu_getMainDir(char *dst, int len); extern void menu_romload_prepare(const char *rom_name); extern void menu_romload_end(void); @@ -51,17 +49,17 @@ static void strlwr_(char* string) while ( (*string++ = (char)tolower(*string)) ); } -static int try_rfn_cut(void) +static int try_rfn_cut(char *fname) { FILE *tmp; char *p; - p = romFileName + strlen(romFileName) - 1; - for (; p > romFileName; p--) + p = fname + strlen(fname) - 1; + for (; p > fname; p--) if (*p == '.') break; *p = 0; - if((tmp = fopen(romFileName, "rb"))) { + if((tmp = fopen(fname, "rb"))) { fclose(tmp); return 1; } @@ -147,20 +145,20 @@ static int emu_isBios(const char *name) static unsigned char id_header[0x100]; -/* checks if romFileName points to valid MegaCD image +/* checks if fname points to valid MegaCD image * if so, checks for suitable BIOS */ -int emu_cdCheck(int *pregion) +int emu_cdCheck(int *pregion, char *fname_in) { unsigned char buf[32]; pm_file *cd_f; int region = 4; // 1: Japan, 4: US, 8: Europe - char ext[5], *fname = romFileName; + char ext[5], *fname = fname_in; cue_track_type type = CT_UNKNOWN; cue_data_t *cue_data = NULL; - get_ext(romFileName, ext); + get_ext(fname_in, ext); if (strcasecmp(ext, ".cue") == 0) { - cue_data = cue_parse(romFileName); + cue_data = cue_parse(fname_in); if (cue_data != NULL) { fname = cue_data->tracks[1].fname; type = cue_data->tracks[1].type; @@ -248,7 +246,7 @@ static int extract_text(char *dest, const unsigned char *src, int len, int swab) char *emu_makeRomId(void) { - static char id_string[3+0x11+0x11+0x30+16]; + static char id_string[3+0xe*3+0x3*3+0x30*3+3]; int pos, swab = 1; if (PicoAHW & PAHW_MCD) { @@ -268,18 +266,36 @@ char *emu_makeRomId(void) return id_string; } -int emu_ReloadRom(void) +// buffer must be at least 150 byte long +void emu_getGameName(char *str150) +{ + int ret, swab = (PicoAHW & PAHW_MCD) ? 0 : 1; + char *s, *d; + + ret = extract_text(str150, 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; +} + +// note: this function might mangle rom_fname +int emu_ReloadRom(char *rom_fname) { unsigned int rom_size = 0; - char *used_rom_name = romFileName; + char *used_rom_name = rom_fname; unsigned char *rom_data = NULL; char ext[5]; pm_file *rom = NULL; int ret, cd_state, cd_region, cfg_loaded = 0; - lprintf("emu_ReloadRom(%s)\n", romFileName); + lprintf("emu_ReloadRom(%s)\n", rom_fname); - get_ext(romFileName, ext); + get_ext(rom_fname, ext); // detect wrong extensions if (!strcmp(ext, ".srm") || !strcmp(ext, "s.gz") || !strcmp(ext, ".mds")) { // s.gz ~ .mds.gz @@ -298,7 +314,7 @@ int emu_ReloadRom(void) { // check for both gmv and rom int dummy; - FILE *movie_file = fopen(romFileName, "rb"); + FILE *movie_file = fopen(rom_fname, "rb"); if(!movie_file) { sprintf(menuErrorMsg, "Failed to open movie."); return 0; @@ -323,29 +339,29 @@ int emu_ReloadRom(void) sprintf(menuErrorMsg, "Invalid GMV file."); return 0; } - dummy = try_rfn_cut() || try_rfn_cut(); + dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname); if (!dummy) { sprintf(menuErrorMsg, "Could't find a ROM for movie."); return 0; } - get_ext(romFileName, ext); + get_ext(rom_fname, ext); } - else if (!strcmp(ext, ".pat")) { + else if (!strcmp(ext, ".pat")) + { int dummy; - PicoPatchLoad(romFileName); - dummy = try_rfn_cut() || try_rfn_cut(); + PicoPatchLoad(rom_fname); + dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname); if (!dummy) { sprintf(menuErrorMsg, "Could't find a ROM to patch."); return 0; } - get_ext(romFileName, ext); + get_ext(rom_fname, ext); } - if ((PicoAHW & PAHW_MCD) && Pico_mcd != NULL) - Stop_CD(); + emu_shutdownMCD(); // check for MegaCD image - cd_state = emu_cdCheck(&cd_region); + cd_state = emu_cdCheck(&cd_region, rom_fname); if (cd_state >= 0 && cd_state != CIT_NOT_CD) { PicoAHW |= PAHW_MCD; @@ -422,7 +438,7 @@ int emu_ReloadRom(void) // insert CD if it was detected if (cd_state != CIT_NOT_CD) { - ret = Insert_CD(romFileName, cd_state); + ret = Insert_CD(rom_fname, cd_state); if (ret != 0) { sprintf(menuErrorMsg, "Insert_CD() failed, invalid CD image?"); lprintf("%s\n", menuErrorMsg); @@ -468,11 +484,11 @@ int emu_ReloadRom(void) emu_noticeMsgUpdated(); // load SRAM for this ROM - if (currentConfig.EmuOpt & 1) + if (currentConfig.EmuOpt & EOPT_USE_SRAM) emu_SaveLoadGame(1, 1); - strncpy(lastRomFile, romFileName, sizeof(lastRomFile)-1); - lastRomFile[sizeof(lastRomFile)-1] = 0; + strncpy(loadedRomFName, rom_fname, sizeof(loadedRomFName)-1); + loadedRomFName[sizeof(loadedRomFName)-1] = 0; rom_loaded = 1; return 1; @@ -484,18 +500,30 @@ fail: } +void emu_shutdownMCD(void) +{ + if ((PicoAHW & PAHW_MCD) && Pico_mcd != NULL) + Stop_CD(); + PicoAHW &= ~PAHW_MCD; +} + static void romfname_ext(char *dst, const char *prefix, const char *ext) { char *p; int prefix_len = 0; // make save filename - for (p = romFileName+strlen(romFileName)-1; p >= romFileName && *p != '/'; p--); p++; + p = loadedRomFName+strlen(loadedRomFName)-1; + for (; p >= loadedRomFName && *p != PATH_SEP_C; p--); p++; *dst = 0; if (prefix) { - strcpy(dst, prefix); - prefix_len = strlen(prefix); + int len = emu_getMainDir(dst, 512); + strcpy(dst + len, prefix); + prefix_len = len + strlen(prefix); } +#ifdef UIQ3 + else p = loadedRomFName; // backward compatibility +#endif strncpy(dst + prefix_len, p, 511-prefix_len); dst[511-8] = 0; if (dst[strlen(dst)-4] == '.') dst[strlen(dst)-4] = 0; @@ -505,7 +533,9 @@ static void romfname_ext(char *dst, const char *prefix, const char *ext) static void make_config_cfg(char *cfg) { - strncpy(cfg, PicoConfigFile, 511); + int len; + len = emu_getMainDir(cfg, 512); + strncpy(cfg + len, PicoConfigFile, 512-6-1-len); if (config_slot != 0) { char *p = strrchr(cfg, '.'); @@ -515,10 +545,34 @@ static void make_config_cfg(char *cfg) cfg[511] = 0; } +void emu_packConfig(void) +{ + currentConfig.s_PicoOpt = PicoOpt; + currentConfig.s_PsndRate = PsndRate; + currentConfig.s_PicoRegion = PicoRegionOverride; + currentConfig.s_PicoAutoRgnOrder = PicoAutoRgnOrder; + currentConfig.s_PicoCDBuffers = PicoCDBuffers; +} + +void emu_unpackConfig(void) +{ + PicoOpt = currentConfig.s_PicoOpt; + PsndRate = currentConfig.s_PsndRate; + PicoRegionOverride = currentConfig.s_PicoRegion; + PicoAutoRgnOrder = currentConfig.s_PicoAutoRgnOrder; + PicoCDBuffers = currentConfig.s_PicoCDBuffers; +} + +static void emu_setDefaultConfig(void) +{ + memcpy(¤tConfig, &defaultConfig, sizeof(currentConfig)); + emu_unpackConfig(); +} + + int emu_ReadConfig(int game, int no_defaults) { char cfg[512]; - FILE *f; int ret; if (!game) @@ -554,37 +608,7 @@ int emu_ReadConfig(int game, int no_defaults) ret = config_readsect("game_def.cfg", sect); } - if (ret != 0) - { - // fall back to old game specific cfg - char extbuf[16]; - if (config_slot != 0) - sprintf(extbuf, ".%i.pbcfg", config_slot); - else strcpy(extbuf, ".pbcfg"); - romfname_ext(cfg, "cfg/", extbuf); - f = fopen(cfg, "rb"); - if (!f) { - romfname_ext(cfg, NULL, ".pbcfg"); - f = fopen(cfg, "rb"); - } - if (f) { - int bread; - fseek(f, 512, SEEK_SET); // skip unused lrom buffer - bread = fread(¤tConfig, 1, sizeof(currentConfig), f); - lprintf("emu_ReadConfig: %s %s\n", cfg, bread > 0 ? "(ok)" : "(failed)"); - fclose(f); - ret = 0; - } - - if (ret == 0) { - PicoOpt = currentConfig.s_PicoOpt; - PsndRate = currentConfig.s_PsndRate; - PicoRegionOverride = currentConfig.s_PicoRegion; - PicoAutoRgnOrder = currentConfig.s_PicoAutoRgnOrder; - // PicoCDBuffers = currentConfig.s_PicoCDBuffers; // ignore in this case - } - } - else + if (ret == 0) { lprintf("loaded cfg from sect \"%s\"\n", sect); } @@ -618,14 +642,7 @@ int emu_WriteConfig(int is_game) if (!is_game) { - strncpy(cfg, PicoConfigFile, 511); - if (config_slot != 0) - { - char *p = strrchr(cfg, '.'); - if (p == NULL) p = cfg + strlen(cfg); - sprintf(p, ".%i.cfg", config_slot); - } - cfg[511] = 0; + make_config_cfg(cfg); write_lrom = 1; } else { if (config_slot != 0) @@ -648,6 +665,7 @@ int emu_WriteConfig(int is_game) } +#ifndef UIQ3 void emu_textOut8(int x, int y, const char *text) { int i,l,len=strlen(text); @@ -694,6 +712,7 @@ void emu_textOut16(int x, int y, const char *text) screen += 8; } } +#endif #ifdef PSP #define MAX_COMBO_KEY 23 @@ -802,7 +821,7 @@ char *emu_GetSaveFName(int load, int is_sram, int slot) if (is_sram) { - romfname_ext(saveFname, (PicoAHW&1) ? "brm/" : "srm/", (PicoAHW&1) ? ".brm" : ".srm"); + romfname_ext(saveFname, (PicoAHW&1) ? "brm"PATH_SEP : "srm"PATH_SEP, (PicoAHW&1) ? ".brm" : ".srm"); if (load) { if (try_ropen_file(saveFname)) return saveFname; // try in current dir.. @@ -815,19 +834,21 @@ char *emu_GetSaveFName(int load, int is_sram, int slot) { ext[0] = 0; if(slot > 0 && slot < 10) sprintf(ext, ".%i", slot); - strcat(ext, (currentConfig.EmuOpt & 8) ? ".mds.gz" : ".mds"); + strcat(ext, (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds.gz" : ".mds"); - romfname_ext(saveFname, "mds/", ext); + romfname_ext(saveFname, "mds" PATH_SEP, ext); if (load) { if (try_ropen_file(saveFname)) return saveFname; romfname_ext(saveFname, NULL, ext); if (try_ropen_file(saveFname)) return saveFname; - if (currentConfig.EmuOpt & 8) { + // no gzipped states, search for non-gzipped + if (currentConfig.EmuOpt & EOPT_GZIP_SAVES) + { ext[0] = 0; if(slot > 0 && slot < 10) sprintf(ext, ".%i", slot); strcat(ext, ".mds"); - romfname_ext(saveFname, "mds/", ext); + romfname_ext(saveFname, "mds"PATH_SEP, ext); if (try_ropen_file(saveFname)) return saveFname; romfname_ext(saveFname, NULL, ext); if (try_ropen_file(saveFname)) return saveFname; diff --git a/platform/common/emu.h b/platform/common/emu.h index 7c5e89b..555df3a 100644 --- a/platform/common/emu.h +++ b/platform/common/emu.h @@ -3,7 +3,18 @@ // For commercial use, separate licencing terms must be obtained. -typedef struct { +#include "port_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define EOPT_USE_SRAM (1<<0) +#define EOPT_SHOW_FPS (1<<1) +#define EOPT_EN_SOUND (1<<2) +#define EOPT_GZIP_SAVES (1<<3) + +typedef struct _currentConfig_t { // char lastRomFile[512]; int EmuOpt; // LSb->MSb: use_sram, show_fps, enable_sound, gzip_saves, // squidgehack, no_save_cfg_on_exit, , 16_bit_mode @@ -13,15 +24,18 @@ typedef struct { int s_PicoOpt; // for old cfg files only int s_PsndRate; int s_PicoRegion; + int s_PicoAutoRgnOrder; + int s_PicoCDBuffers; int Frameskip; int CPUclock; - int KeyBinds[32]; + int KeyBinds[PLAT_MAX_KEYS]; int volume; int gamma; +#if PLAT_HAVE_JOY int JoyBinds[4][32]; - int s_PicoAutoRgnOrder; - int s_PicoCDBuffers; - int scaling; // gp2x: 0=center, 1=hscale, 2=hvscale, 3=hsoftscale; psp: bilinear filtering +#endif + int scaling; // gp2x: 0=center, 1=hscale, 2=hvscale, 3=hsoftscale; psp: bilinear filtering + int rotation; // for UIQ float scale; // psp: screen scale float hscale32, hscale40; // psp: horizontal scale int gamma2; // psp: black level @@ -35,12 +49,12 @@ extern char noticeMsg[64]; extern int state_slot; extern int config_slot, config_slot_current; extern unsigned char *movie_data; -extern char lastRomFile[512]; +extern char loadedRomFName[512]; // last loaded ROM filename extern int kb_combo_keys, kb_combo_acts; // keys and actions which need button combos extern int pico_inp_mode; -int emu_ReloadRom(void); +int emu_ReloadRom(char *rom_fname); int emu_SaveLoadGame(int load, int sram); int emu_ReadConfig(int game, int no_defaults); int emu_WriteConfig(int game); @@ -48,17 +62,26 @@ char *emu_GetSaveFName(int load, int is_sram, int slot); int emu_checkSaveFile(int slot); void emu_setSaveStateCbs(int gz); void emu_updateMovie(void); -int emu_cdCheck(int *pregion); +int emu_cdCheck(int *pregion, char *fname_in); int emu_findBios(int region, char **bios_file); void emu_textOut8 (int x, int y, const char *text); void emu_textOut16(int x, int y, const char *text); char *emu_makeRomId(void); +void emu_getGameName(char *str150); void emu_findKeyBindCombos(void); void emu_forcedFrame(int opts); void emu_changeFastForward(int set_on); void emu_RunEventsPico(unsigned int events); void emu_DoTurbo(int *pad, int acts); +void emu_packConfig(void); +void emu_unpackConfig(void); +void emu_shutdownMCD(void); extern const char * const keyNames[]; void emu_prepareDefaultConfig(void); void emu_platformDebugCat(char *str); + +#ifdef __cplusplus +} // extern "C" +#endif + diff --git a/platform/common/menu.c b/platform/common/menu.c index 0f3fb61..169b892 100644 --- a/platform/common/menu.c +++ b/platform/common/menu.c @@ -39,6 +39,8 @@ me_bind_action me_ctrl_actions[15] = }; +#ifndef UIQ3 + static unsigned char menu_font_data[10240]; static int menu_text_color = 0xffff; // default to white static int menu_sel_color = -1; // disabled @@ -331,28 +333,6 @@ int me_process(menu_entry *entries, int count, menu_id id, int is_next) } } -const char *me_region_name(unsigned int code, int auto_order) -{ - static const char *names[] = { "Auto", " Japan NTSC", " Japan PAL", " USA", " Europe" }; - static const char *names_short[] = { "", " JP", " JP", " US", " EU" }; - int u, i = 0; - if (code) { - code <<= 1; - while((code >>= 1)) i++; - if (i > 4) return "unknown"; - return names[i]; - } else { - static char name[24]; - strcpy(name, "Auto:"); - for (u = 0; u < 3; u++) { - i = 0; code = ((auto_order >> u*4) & 0xf) << 1; - while((code >>= 1)) i++; - strcat(name, names_short[i]); - } - return name; - } -} - // ------------ debug menu ------------ #include @@ -472,4 +452,30 @@ void debug_menu_loop(void) } } +#endif // !UIQ3 + +// ------------ util ------------ + +const char *me_region_name(unsigned int code, int auto_order) +{ + static const char *names[] = { "Auto", " Japan NTSC", " Japan PAL", " USA", " Europe" }; + static const char *names_short[] = { "", " JP", " JP", " US", " EU" }; + int u, i = 0; + if (code) { + code <<= 1; + while((code >>= 1)) i++; + if (i > 4) return "unknown"; + return names[i]; + } else { + static char name[24]; + strcpy(name, "Auto:"); + for (u = 0; u < 3; u++) { + i = 0; code = ((auto_order >> u*4) & 0xf) << 1; + while((code >>= 1)) i++; + strcat(name, names_short[i]); + } + return name; + } +} + diff --git a/platform/common/menu.h b/platform/common/menu.h index 7a4213d..b17f2dd 100644 --- a/platform/common/menu.h +++ b/platform/common/menu.h @@ -52,6 +52,7 @@ typedef enum MA_OPT_SAVECFG_GAME, MA_OPT_LOADCFG, MA_OPT_INTERLACED, /* giz */ + MA_OPT_ROTATION, /* uiq */ MA_OPT2_GAMMA, MA_OPT2_A_SN_GAMMA, MA_OPT2_DBLBUFF, /* giz */ diff --git a/platform/gizmondo/Makefile b/platform/gizmondo/Makefile index e75c71c..0e37e2d 100644 --- a/platform/gizmondo/Makefile +++ b/platform/gizmondo/Makefile @@ -28,8 +28,9 @@ ifeq "$(profile)" "2" COPT_COMMON += -fprofile-use endif CFLAGS = $(COPT_COMMON) -mcpu=arm920t -mtune=arm920t +SFLAGS = $(CFLAGS) ASFLAGS = -mcpu=arm920t -mfloat-abi=soft -GCC = $(CROSS)gcc +CC = $(CROSS)gcc STRIP = $(CROSS)strip AS = $(CROSS)as LD = $(CROSS)ld @@ -115,7 +116,7 @@ all: mkdirs PicoDrive.exe readme.txt PicoDrive.exe : $(OBJS) ../common/helix/$(CROSS)helix-mp3.a @echo ">>>" $@ - $(GCC) -o $@ -static $(CFLAGS) $^ -lm -lpng -Lkgsdk/ -lKGSDK -Wl,-Map=PicoDrive.map \ + $(CC) -o $@ -static $(CFLAGS) $^ -lm -lpng -Lkgsdk/ -lKGSDK -Wl,-Map=PicoDrive.map \ 2>&1 | grep -v ".idata$$4" # | grep -v "supports interworking, whereas" ifeq ($(DEBUG),) $(STRIP) $@ @@ -125,66 +126,10 @@ ifeq "$(profile)" "1" endif -.c.o: - @echo ">>>" $< - $(GCC) $(CFLAGS) $(DEFINC) -c $< -o $@ -#.s.o: -# @echo $< -# $(GCC) $(CFLAGS) $(DEFINC) -c $< -o $@ - -mkdirs: - mkdir -p $(DIRS) - -Pico/carthw/svp/compiler.o : ../../Pico/carthw/svp/ssp16.o ../../Pico/carthw/svp/gen_arm.c - -Pico/draw_asm.o : ../../Pico/Draw.s - @echo ">>>" $@ - @$(AS) $(ASFLAGS) $< -o $@ -Pico/draw2_asm.o : ../../Pico/Draw2.s - @echo ">>>" $@ - @$(AS) $(ASFLAGS) $< -o $@ -Pico/memory_asm.o : ../../Pico/Memory.s - @echo ">>>" $@ - @$(AS) $(ASFLAGS) $< -o $@ -Pico/sound/ym2612_asm.o : ../../Pico/sound/ym2612.s - @echo ">>>" $@ - @$(AS) $(ASFLAGS) $< -o $@ -Pico/sound/mix_asm.o : ../../Pico/sound/mix.s - @echo ">>>" $@ - @$(AS) $(ASFLAGS) $< -o $@ -Pico/misc_asm.o : ../../Pico/Misc.s - @echo ">>>" $@ - @$(AS) $(ASFLAGS) $< -o $@ -Pico/cd/pico_asm.o : ../../Pico/cd/Pico.s - @echo ">>>" $@ - @$(AS) $(ASFLAGS) $< -o $@ -Pico/cd/memory_asm.o : ../../Pico/cd/Memory.s - @echo ">>>" $@ - @$(AS) $(ASFLAGS) $< -o $@ -Pico/cd/misc_asm.o : ../../Pico/cd/Misc.s - @echo ">>>" $@ - @$(AS) $(ASFLAGS) $< -o $@ -Pico/carthw/svp/stub_arm.o : ../../Pico/carthw/svp/stub_arm.S - @echo ">>>" $@ - $(GCC) $(CFLAGS) $(DEFINC) -c $< -o $@ - -# build Cyclone -cpu/Cyclone/proj/Cyclone.s: - @echo building Cyclone... - @make -C ../../cpu/Cyclone/proj CONFIG_FILE=config_pico.h - -Pico/Pico.o Pico/cd/Pico.o: ../../Pico/PicoFrameHints.c ../../Pico/PicoInt.h -Pico/Memory.o Pico/cd/Memory.o : ../../Pico/MemoryCmn.c ../../Pico/PicoInt.h - -# build helix libs -../common/helix/$(CROSS)helix-mp3.a: - make -C ../common/helix clean all - readme.txt: ../../tools/textfilter ../base_readme.txt ../../tools/textfilter ../base_readme.txt $@ GIZ -../../tools/textfilter: ../../tools/textfilter.c - make -C ../../tools/ textfilter +include ../common/common_arm.mak # cleanup @@ -194,10 +139,6 @@ tidy: @$(RM) $(OBJS) -clean_prof: - find ../.. -name '*.gcno' -delete - find ../.. -name '*.gcda' -delete - up: PicoDrive.exe synce-pcp -d 3 PicoDrive.exe ":/SD Card/emus/PicoDrive/PicoDrive.exe" diff --git a/platform/gizmondo/emu.c b/platform/gizmondo/emu.c index cde2e47..ee5a105 100644 --- a/platform/gizmondo/emu.c +++ b/platform/gizmondo/emu.c @@ -45,9 +45,11 @@ void emu_noticeMsgUpdated(void) noticeMsgTime = GetTickCount(); } -void emu_getMainDir(char *dst, int len) +int emu_getMainDir(char *dst, int len) { if (len > 0) *dst = 0; + + return 0; } static void emu_msg_cb(const char *msg) @@ -143,16 +145,6 @@ void emu_prepareDefaultConfig(void) defaultConfig.turbo_rate = 15; } -void emu_setDefaultConfig(void) -{ - memcpy(¤tConfig, &defaultConfig, sizeof(currentConfig)); - PicoOpt = currentConfig.s_PicoOpt; - PsndRate = currentConfig.s_PsndRate; - PicoRegionOverride = currentConfig.s_PicoRegion; - PicoAutoRgnOrder = currentConfig.s_PicoAutoRgnOrder; - PicoCDBuffers = currentConfig.s_PicoCDBuffers; -} - static int EmuScanBegin16(unsigned int num) { diff --git a/platform/gizmondo/main.c b/platform/gizmondo/main.c index 7b2dc91..801fbc8 100644 --- a/platform/gizmondo/main.c +++ b/platform/gizmondo/main.c @@ -34,7 +34,7 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdL break; case PGS_ReloadRom: - if (emu_ReloadRom()) + if (emu_ReloadRom(romFileName)) engineState = PGS_Running; else { lprintf("PGS_ReloadRom == 0\n"); diff --git a/platform/gizmondo/menu.c b/platform/gizmondo/menu.c index 9643a16..50de347 100644 --- a/platform/gizmondo/menu.c +++ b/platform/gizmondo/menu.c @@ -813,7 +813,7 @@ menu_entry cdopt_entries[] = { NULL, MB_NONE, MA_CDOPT_TESTBIOS_EUR, NULL, 0, 0, 0, 1, 0 }, { NULL, MB_NONE, MA_CDOPT_TESTBIOS_JAP, NULL, 0, 0, 0, 1, 0 }, { "CD LEDs", MB_ONOFF, MA_CDOPT_LEDS, ¤tConfig.EmuOpt, 0x0400, 0, 0, 1, 1 }, - { "CDDA audio (using mp3s)", MB_ONOFF, MA_CDOPT_CDDA, &PicoOpt, 0x0800, 0, 0, 1, 1 }, + { "CDDA audio", MB_ONOFF, MA_CDOPT_CDDA, &PicoOpt, 0x0800, 0, 0, 1, 1 }, { "PCM audio", MB_ONOFF, MA_CDOPT_PCM, &PicoOpt, 0x0400, 0, 0, 1, 1 }, { NULL, MB_NONE, MA_CDOPT_READAHEAD, NULL, 0, 0, 0, 1, 1 }, { "SaveRAM cart", MB_ONOFF, MA_CDOPT_SAVERAM, &PicoOpt, 0x8000, 0, 0, 1, 1 }, @@ -1438,10 +1438,10 @@ static void menu_loop_root(void) { char curr_path[MAX_PATH], *selfname; FILE *tstf; - if ( (tstf = fopen(lastRomFile, "rb")) ) + if ( (tstf = fopen(loadedRomFName, "rb")) ) { fclose(tstf); - strcpy(curr_path, lastRomFile); + strcpy(curr_path, loadedRomFName); } else getcwd(curr_path, MAX_PATH); @@ -1586,10 +1586,10 @@ int menu_loop_tray(void) menu_gfx_prepare(); - if ( (tstf = fopen(lastRomFile, "rb")) ) + if ( (tstf = fopen(loadedRomFName, "rb")) ) { fclose(tstf); - strcpy(curr_path, lastRomFile); + strcpy(curr_path, loadedRomFName); } else { @@ -1613,7 +1613,7 @@ int menu_loop_tray(void) if (selfname) { int ret = -1; cd_img_type cd_type; - cd_type = emu_cdCheck(NULL); + cd_type = emu_cdCheck(NULL, romFileName); if (cd_type != CIT_NOT_CD) ret = Insert_CD(romFileName, cd_type); if (ret != 0) { diff --git a/platform/gizmondo/port_config.h b/platform/gizmondo/port_config.h index d913855..0079f68 100644 --- a/platform/gizmondo/port_config.h +++ b/platform/gizmondo/port_config.h @@ -24,4 +24,10 @@ //#define dprintf(f,...) printf("%05i:%03i: " f "\n",Pico.m.frame_count,Pico.m.scanline,##__VA_ARGS__) #define dprintf(x...) +// platform +#define PLAT_MAX_KEYS 32 +#define PLAT_HAVE_JOY 0 +#define PATH_SEP "/" // because of cegcc +#define PATH_SEP_C '/' + #endif //PORT_CONFIG_H diff --git a/platform/gp2x/Makefile b/platform/gp2x/Makefile index 57bd810..06761a3 100644 --- a/platform/gp2x/Makefile +++ b/platform/gp2x/Makefile @@ -44,16 +44,17 @@ endif ifeq "$(profile)" "2" COPT_COMMON += -fprofile-use endif -COPT = $(COPT_COMMON) -mtune=arm920t -ASOPT = -mcpu=arm920t -mfloat-abi=soft -GCC = $(CROSS)gcc +CFLAGS = $(COPT_COMMON) -mcpu=arm920t -mtune=arm920t +SFLAGS = $(CFLAGS) +ASFLAGS = -mcpu=arm920t -mfloat-abi=soft +CC = $(CROSS)gcc STRIP = $(CROSS)strip AS = $(CROSS)as LD = $(CROSS)ld OBJCOPY = $(CROSS)objcopy # frontend -OBJS += main.o menu.o gp2x.o usbjoy.o emu.o squidgehack.o cpuctrl.o +OBJS += main.o menu.o gp2x.o usbjoy.o emu.o squidgehack.o cpuctrl.o asm_utils.o # 940 core control OBJS += 940ctl.o @@ -149,7 +150,7 @@ all: mkdirs PicoDrive.gpe PicoDrive.gpe : $(OBJS) ../common/helix/$(CROSS)helix-mp3.a @echo ">>>" $@ - $(GCC) -o $@ $(COPT) $^ -lm -lpng -Wl,-Map=PicoDrive.map + $(CC) -o $@ $(CFLAGS) $^ -lm -lpng -Wl,-Map=PicoDrive.map ifeq ($(DEBUG),) $(STRIP) $@ endif @@ -168,75 +169,10 @@ tidy: # @make -C ../../cpu/Cyclone/proj -f Makefile.linux clean -clean_prof: - find ../.. -name '*.gcno' -delete - find ../.. -name '*.gcda' -delete - - -mkdirs: - mkdir -p $(DIRS) - -.c.o: - @echo ">>>" $< - $(GCC) $(COPT) $(DEFINC) -c $< -o $@ -.s.o: - @echo ">>>" $< - $(GCC) $(COPT) $(DEFINC) -c $< -o $@ -.S.o: - @echo ">>>" $< - $(GCC) $(COPT) $(DEFINC) -c $< -o $@ - -Pico/carthw/svp/compiler.o : ../../Pico/carthw/svp/ssp16.o ../../Pico/carthw/svp/gen_arm.c - -Pico/draw_asm.o : ../../Pico/Draw.s - @echo ">>>" $< - $(AS) $(ASOPT) $< -o $@ -Pico/draw2_asm.o : ../../Pico/Draw2.s - @echo ">>>" $< - $(AS) $(ASOPT) $< -o $@ -Pico/memory_asm.o : ../../Pico/Memory.s - @echo ">>>" $< - $(AS) $(ASOPT) $< -o $@ -Pico/sound/ym2612_asm.o : ../../Pico/sound/ym2612.s - @echo ">>>" $< - $(AS) $(ASOPT) $< -o $@ -Pico/sound/mix_asm.o : ../../Pico/sound/mix.s - @echo ">>>" $< - $(AS) $(ASOPT) $< -o $@ -Pico/misc_asm.o : ../../Pico/Misc.s - @echo ">>>" $< - $(AS) $(ASOPT) $< -o $@ -Pico/cd/pico_asm.o : ../../Pico/cd/Pico.s - @echo ">>>" $< - $(AS) $(ASOPT) $< -o $@ -Pico/cd/memory_asm.o : ../../Pico/cd/Memory.s - @echo ">>>" $< - $(AS) $(ASOPT) $< -o $@ -Pico/cd/misc_asm.o : ../../Pico/cd/Misc.s - @echo ">>>" $< - $(AS) $(ASOPT) $< -o $@ - -# build Cyclone -../../cpu/Cyclone/proj/Cyclone.s : - @echo building Cyclone... - @make -C ../../cpu/Cyclone/proj CONFIG_FILE=config_pico.h - -../../cpu/musashi/m68kops.c : - @make -C ../../cpu/musashi - -Pico/Pico.o Pico/cd/Pico.o: ../../Pico/PicoFrameHints.c ../../Pico/PicoInt.h -Pico/Memory.o Pico/cd/Memory.o : ../../Pico/MemoryCmn.c ../../Pico/PicoInt.h - -# build helix libs -../common/helix/$(CROSS)helix-mp3.a: - make -C ../common/helix clean all - readme.txt: ../../tools/textfilter ../base_readme.txt ../../tools/textfilter ../base_readme.txt $@ GP2X -../../tools/textfilter: ../../tools/textfilter.c - make -C ../../tools/ textfilter - +include ../common/common_arm.mak # ----------- release ----------- ifneq ($(findstring rel,$(MAKECMDGOALS)),) diff --git a/platform/gp2x/emu.c b/platform/gp2x/emu.c index 12d70e3..c225f18 100644 --- a/platform/gp2x/emu.c +++ b/platform/gp2x/emu.c @@ -49,7 +49,6 @@ static short __attribute__((aligned(4))) sndBuffer[2*44100/50]; static struct timeval noticeMsgTime = { 0, 0 }; // when started showing static int osd_fps_x; static int gp2x_old_gamma = 100; -char noticeMsg[64]; // notice msg to draw unsigned char *PicoDraw2FB = NULL; // temporary buffer for alt renderer int reset_timing = 0; @@ -66,7 +65,7 @@ void emu_noticeMsgUpdated(void) gettimeofday(¬iceMsgTime, 0); } -void emu_getMainDir(char *dst, int len) +int emu_getMainDir(char *dst, int len) { extern char **g_argv; int j; @@ -77,6 +76,8 @@ void emu_getMainDir(char *dst, int len) dst[len] = 0; for (j = strlen(dst); j > 0; j--) if (dst[j] == '/') { dst[j+1] = 0; break; } + + return j + 1; } void emu_Init(void) @@ -167,16 +168,6 @@ void emu_prepareDefaultConfig(void) defaultConfig.turbo_rate = 15; } -void emu_setDefaultConfig(void) -{ - memcpy(¤tConfig, &defaultConfig, sizeof(currentConfig)); - PicoOpt = currentConfig.s_PicoOpt; - PsndRate = currentConfig.s_PsndRate; - PicoRegionOverride = currentConfig.s_PicoRegion; - PicoAutoRgnOrder = currentConfig.s_PicoAutoRgnOrder; - PicoCDBuffers = currentConfig.s_PicoCDBuffers; -} - void osd_text(int x, int y, const char *text) { int len = strlen(text)*8; diff --git a/platform/gp2x/main.c b/platform/gp2x/main.c index 50210f6..bdf90d5 100644 --- a/platform/gp2x/main.c +++ b/platform/gp2x/main.c @@ -118,7 +118,7 @@ int main(int argc, char *argv[]) if (engineState == PGS_ReloadRom) { - if (emu_ReloadRom()) { + if (emu_ReloadRom(romFileName)) { engineState = PGS_Running; if (load_state_slot >= 0) { state_slot = load_state_slot; @@ -136,7 +136,7 @@ int main(int argc, char *argv[]) break; case PGS_ReloadRom: - if (emu_ReloadRom()) + if (emu_ReloadRom(romFileName)) engineState = PGS_Running; else { printf("PGS_ReloadRom == 0\n"); diff --git a/platform/gp2x/menu.c b/platform/gp2x/menu.c index 1dc829a..b7bd3f1 100644 --- a/platform/gp2x/menu.c +++ b/platform/gp2x/menu.c @@ -901,7 +901,7 @@ menu_entry cdopt_entries[] = { NULL, MB_NONE, MA_CDOPT_TESTBIOS_EUR, NULL, 0, 0, 0, 1, 0 }, { NULL, MB_NONE, MA_CDOPT_TESTBIOS_JAP, NULL, 0, 0, 0, 1, 0 }, { "CD LEDs", MB_ONOFF, MA_CDOPT_LEDS, ¤tConfig.EmuOpt, 0x0400, 0, 0, 1, 1 }, - { "CDDA audio (using mp3s)", MB_ONOFF, MA_CDOPT_CDDA, &PicoOpt, 0x0800, 0, 0, 1, 1 }, + { "CDDA audio", MB_ONOFF, MA_CDOPT_CDDA, &PicoOpt, 0x0800, 0, 0, 1, 1 }, { "PCM audio", MB_ONOFF, MA_CDOPT_PCM, &PicoOpt, 0x0400, 0, 0, 1, 1 }, { NULL, MB_NONE, MA_CDOPT_READAHEAD, NULL, 0, 0, 0, 1, 1 }, { "SaveRAM cart", MB_ONOFF, MA_CDOPT_SAVERAM, &PicoOpt, 0x8000, 0, 0, 1, 1 }, @@ -1551,10 +1551,10 @@ static void menu_loop_root(void) { char curr_path[PATH_MAX], *selfname; FILE *tstf; - if ( (tstf = fopen(lastRomFile, "rb")) ) + if ( (tstf = fopen(loadedRomFName, "rb")) ) { fclose(tstf); - strcpy(curr_path, lastRomFile); + strcpy(curr_path, loadedRomFName); } else getcwd(curr_path, PATH_MAX); @@ -1694,10 +1694,10 @@ int menu_loop_tray(void) gp2x_memset_all_buffers(0, 0, 320*240*2); menu_gfx_prepare(); - if ( (tstf = fopen(lastRomFile, "rb")) ) + if ( (tstf = fopen(loadedRomFName, "rb")) ) { fclose(tstf); - strcpy(curr_path, lastRomFile); + strcpy(curr_path, loadedRomFName); } else { @@ -1721,7 +1721,7 @@ int menu_loop_tray(void) if (selfname) { int ret = -1; cd_img_type cd_type; - cd_type = emu_cdCheck(NULL); + cd_type = emu_cdCheck(NULL, romFileName); if (cd_type != CIT_NOT_CD) ret = Insert_CD(romFileName, cd_type); if (ret != 0) { diff --git a/platform/gp2x/port_config.h b/platform/gp2x/port_config.h index 22b43cd..9dc2c6a 100644 --- a/platform/gp2x/port_config.h +++ b/platform/gp2x/port_config.h @@ -24,4 +24,10 @@ //#define dprintf(f,...) printf("%05i:%03i: " f "\n",Pico.m.frame_count,Pico.m.scanline,##__VA_ARGS__) #define dprintf(x...) +// platform +#define PLAT_MAX_KEYS 32 +#define PLAT_HAVE_JOY 1 +#define PATH_SEP "/" +#define PATH_SEP_C '/' + #endif //PORT_CONFIG_H diff --git a/platform/pandora/emu.c b/platform/pandora/emu.c index 633d027..2d873df 100644 --- a/platform/pandora/emu.c +++ b/platform/pandora/emu.c @@ -48,7 +48,6 @@ char romFileName[PATH_MAX]; static short __attribute__((aligned(4))) sndBuffer[2*44100/50]; static struct timeval noticeMsgTime = { 0, 0 }; // when started showing static int osd_fps_x; -char noticeMsg[64]; // notice msg to draw unsigned char *PicoDraw2FB = NULL; // temporary buffer for alt renderer int reset_timing = 0; @@ -65,7 +64,7 @@ void emu_noticeMsgUpdated(void) gettimeofday(¬iceMsgTime, 0); } -void emu_getMainDir(char *dst, int len) +int emu_getMainDir(char *dst, int len) { extern char **g_argv; int j; @@ -76,6 +75,8 @@ void emu_getMainDir(char *dst, int len) dst[len] = 0; for (j = strlen(dst); j > 0; j--) if (dst[j] == '/') { dst[j+1] = 0; break; } + + return j + 1; } void emu_Init(void) @@ -151,16 +152,6 @@ void emu_prepareDefaultConfig(void) defaultConfig.turbo_rate = 15; } -void emu_setDefaultConfig(void) -{ - memcpy(¤tConfig, &defaultConfig, sizeof(currentConfig)); - PicoOpt = currentConfig.s_PicoOpt; - PsndRate = currentConfig.s_PsndRate; - PicoRegionOverride = currentConfig.s_PicoRegion; - PicoAutoRgnOrder = currentConfig.s_PicoAutoRgnOrder; - PicoCDBuffers = currentConfig.s_PicoCDBuffers; -} - static void textOut16(int x, int y, const char *text) { int i,l,len=strlen(text); diff --git a/platform/pandora/main.c b/platform/pandora/main.c index 010bff9..5fecb4a 100644 --- a/platform/pandora/main.c +++ b/platform/pandora/main.c @@ -92,7 +92,7 @@ int main(int argc, char *argv[]) if (engineState == PGS_ReloadRom) { - if (emu_ReloadRom()) { + if (emu_ReloadRom(romFileName)) { engineState = PGS_Running; if (load_state_slot >= 0) { state_slot = load_state_slot; @@ -110,7 +110,7 @@ int main(int argc, char *argv[]) break; case PGS_ReloadRom: - if (emu_ReloadRom()) + if (emu_ReloadRom(romFileName)) engineState = PGS_Running; else { printf("PGS_ReloadRom == 0\n"); diff --git a/platform/pandora/port_config.h b/platform/pandora/port_config.h index 22b43cd..9dc2c6a 100644 --- a/platform/pandora/port_config.h +++ b/platform/pandora/port_config.h @@ -24,4 +24,10 @@ //#define dprintf(f,...) printf("%05i:%03i: " f "\n",Pico.m.frame_count,Pico.m.scanline,##__VA_ARGS__) #define dprintf(x...) +// platform +#define PLAT_MAX_KEYS 32 +#define PLAT_HAVE_JOY 1 +#define PATH_SEP "/" +#define PATH_SEP_C '/' + #endif //PORT_CONFIG_H diff --git a/platform/psp/emu.c b/platform/psp/emu.c index b050419..d3e6317 100644 --- a/platform/psp/emu.c +++ b/platform/psp/emu.c @@ -54,9 +54,10 @@ void emu_noticeMsgUpdated(void) noticeMsgTime = sceKernelGetSystemTimeLow(); } -void emu_getMainDir(char *dst, int len) +int emu_getMainDir(char *dst, int len) { if (len > 0) *dst = 0; + return 0; } static void osd_text(int x, const char *text, int is_active, int clear_all) @@ -155,16 +156,6 @@ void emu_prepareDefaultConfig(void) defaultConfig.turbo_rate = 15; } -void emu_setDefaultConfig(void) -{ - memcpy(¤tConfig, &defaultConfig, sizeof(currentConfig)); - PicoOpt = currentConfig.s_PicoOpt; - PsndRate = currentConfig.s_PsndRate; - PicoRegionOverride = currentConfig.s_PicoRegion; - PicoAutoRgnOrder = currentConfig.s_PicoAutoRgnOrder; - PicoCDBuffers = currentConfig.s_PicoCDBuffers; -} - extern void amips_clut(unsigned short *dst, unsigned char *src, unsigned short *pal, int count); extern void amips_clut_6bit(unsigned short *dst, unsigned char *src, unsigned short *pal, int count); diff --git a/platform/psp/main.c b/platform/psp/main.c index a2e99aa..e0698d5 100644 --- a/platform/psp/main.c +++ b/platform/psp/main.c @@ -52,13 +52,13 @@ int pico_main(void) #ifndef GPROF menu_loop(); #else - strcpy(romFileName, lastRomFile); + strcpy(romFileName, loadedRomFName); engineState = PGS_ReloadRom; #endif break; case PGS_ReloadRom: - if (emu_ReloadRom()) { + if (emu_ReloadRom(romFileName)) { engineState = PGS_Running; if (mp3_last_error != 0) engineState = PGS_Menu; // send to menu to display mp3 error diff --git a/platform/psp/menu.c b/platform/psp/menu.c index d348a57..0e578b1 100644 --- a/platform/psp/menu.c +++ b/platform/psp/menu.c @@ -825,7 +825,7 @@ menu_entry cdopt_entries[] = { NULL, MB_NONE, MA_CDOPT_TESTBIOS_EUR, NULL, 0, 0, 0, 1, 0 }, { NULL, MB_NONE, MA_CDOPT_TESTBIOS_JAP, NULL, 0, 0, 0, 1, 0 }, { "CD LEDs", MB_ONOFF, MA_CDOPT_LEDS, ¤tConfig.EmuOpt, 0x0400, 0, 0, 1, 1 }, - { "CDDA audio (using mp3s)", MB_ONOFF, MA_CDOPT_CDDA, &PicoOpt, 0x0800, 0, 0, 1, 1 }, + { "CDDA audio", MB_ONOFF, MA_CDOPT_CDDA, &PicoOpt, 0x0800, 0, 0, 1, 1 }, { "PCM audio", MB_ONOFF, MA_CDOPT_PCM, &PicoOpt, 0x0400, 0, 0, 1, 1 }, { NULL, MB_NONE, MA_CDOPT_READAHEAD, NULL, 0, 0, 0, 1, 1 }, { "SaveRAM cart", MB_ONOFF, MA_CDOPT_SAVERAM, &PicoOpt, 0x8000, 0, 0, 1, 1 }, @@ -1632,10 +1632,10 @@ static void menu_loop_root(void) { char curr_path[PATH_MAX], *selfname; FILE *tstf; - if ( (tstf = fopen(lastRomFile, "rb")) ) + if ( (tstf = fopen(loadedRomFName, "rb")) ) { fclose(tstf); - strcpy(curr_path, lastRomFile); + strcpy(curr_path, loadedRomFName); } else getcwd(curr_path, PATH_MAX); @@ -1777,10 +1777,10 @@ int menu_loop_tray(void) menu_gfx_prepare(); - if ( (tstf = fopen(lastRomFile, "rb")) ) + if ( (tstf = fopen(loadedRomFName, "rb")) ) { fclose(tstf); - strcpy(curr_path, lastRomFile); + strcpy(curr_path, loadedRomFName); } else { @@ -1804,7 +1804,7 @@ int menu_loop_tray(void) if (selfname) { int ret = -1; cd_img_type cd_type; - cd_type = emu_cdCheck(NULL); + cd_type = emu_cdCheck(NULL, romFileName); if (cd_type != CIT_NOT_CD) ret = Insert_CD(romFileName, cd_type); if (ret != 0) { diff --git a/platform/psp/port_config.h b/platform/psp/port_config.h index 56513c0..4659b62 100644 --- a/platform/psp/port_config.h +++ b/platform/psp/port_config.h @@ -28,4 +28,10 @@ extern void blit1(void); //#define dprintf(f,...) printf("%05i:%03i: " f "\n",Pico.m.frame_count,Pico.m.scanline,##__VA_ARGS__) #define dprintf(x...) +// platform +#define PLAT_MAX_KEYS 32 +#define PLAT_HAVE_JOY 0 +#define PATH_SEP "/" +#define PATH_SEP_C '/' + #endif //PORT_CONFIG_H diff --git a/platform/uiq3/App.cpp b/platform/uiq3/App.cpp index 74e7d24..68a802c 100644 --- a/platform/uiq3/App.cpp +++ b/platform/uiq3/App.cpp @@ -12,9 +12,9 @@ * *******************************************************************/ -#include "app.h" +#include "App.h" // #include "picodriven.mbg" // bitmap identifiers -#include // resource include +#include "rsc/picodrive.rsg" // resource include #include #include //#include @@ -25,14 +25,15 @@ #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include "Dialogs.h" #include "engine/debug.h" - +#include "../common/emu.h" +#include "emu.h" //////////////////////////////////////////////////////////////// @@ -161,37 +162,37 @@ void CPicolAppView::HandleCommandL(CQikCommand& aCommand) break; case EEikCmdPicoFrameskipAuto: - iCurrentConfig.iFrameskip = TPicoConfig::PFSkipAuto; - iQikAppUi.Document()->SaveL(); + currentConfig.Frameskip = -1; + emu_WriteConfig(0); break; case EEikCmdPicoFrameskip0: - iCurrentConfig.iFrameskip = 0; - iQikAppUi.Document()->SaveL(); + currentConfig.Frameskip = 0; + emu_WriteConfig(0); break; case EEikCmdPicoFrameskip1: - iCurrentConfig.iFrameskip = 1; - iQikAppUi.Document()->SaveL(); + currentConfig.Frameskip = 1; + emu_WriteConfig(0); break; case EEikCmdPicoFrameskip2: - iCurrentConfig.iFrameskip = 2; - iQikAppUi.Document()->SaveL(); + currentConfig.Frameskip = 2; + emu_WriteConfig(0); break; case EEikCmdPicoFrameskip4: - iCurrentConfig.iFrameskip = 4; - iQikAppUi.Document()->SaveL(); + currentConfig.Frameskip = 4; + emu_WriteConfig(0); break; case EEikCmdPicoFrameskip8: - iCurrentConfig.iFrameskip = 8; - iQikAppUi.Document()->SaveL(); + currentConfig.Frameskip = 8; + emu_WriteConfig(0); break; case EEikCmdExit: - iQikAppUi.Document()->SaveL(); + emu_Deinit(); CPicoGameSession::freeResources(); //break; // this is intentional @@ -214,11 +215,14 @@ void CPicolAppView::DisplayOpenROMDialogL() CleanupStack::PushL(fileArray); _LIT16(KDlgTitle, "Select a ROM file"); + TPtrC8 text8((TUint8*) loadedRomFName); + iCurrentConfig.iLastROMFile.Copy(text8); + if( CQikSelectFileDlg::RunDlgLD( *mimeArray, *fileArray, &KDlgTitle, &iCurrentConfig.iLastROMFile) ) { CEikonEnv::Static()->BusyMsgL(_L("Loading ROM")); TPtrC16 file = (*fileArray)[0]; - iCurrentConfig.iLastROMFile.Copy(file); + //iCurrentConfig.iLastROMFile.Copy(file); // push the config first CPicoGameSession::Do(PicoMsgSetAppView, this); @@ -271,7 +275,7 @@ void CPicolAppView::DisplayOpenROMDialogL() CEikonEnv::Static()->InfoWinL(_L("Error"), _L("Failed to start soundsystem, disabled sound.")); break; } - if(res == 6 || res == 7) iCurrentConfig.iFlags &= ~4; + if(res == 6 || res == 7) currentConfig.EmuOpt &= ~EOPT_EN_SOUND; if(iROMLoaded) { if(iTitleAdded) @@ -287,11 +291,13 @@ void CPicolAppView::DisplayOpenROMDialogL() void CPicolAppView::DisplayConfigDialogL() { - CPicoConfigDialog* configDialog = new(ELeave)CPicoConfigDialog(iCurrentConfig); + CPicoConfigDialog* configDialog = new(ELeave)CPicoConfigDialog(currentConfig); + emu_packConfig(); configDialog->ExecuteLD(R_PICO_CONFIG); + emu_unpackConfig(); + emu_WriteConfig(0); - CPicoGameSession::Do(PicoMsgConfigChange, &iCurrentConfig); - iQikAppUi.Document()->SaveL(); + CPicoGameSession::Do(PicoMsgConfigChange, ¤tConfig); } @@ -311,13 +317,13 @@ void CPicolAppView::DisplayAboutDialogL() } #ifdef __DEBUG_PRINT -extern "C" char *debugString(); +extern "C" char *PDebugMain(); #endif void CPicolAppView::DisplayDebugDialogL() { #ifdef __DEBUG_PRINT - CDebugDialog* dialog = new (ELeave) CDebugDialog(debugString()); + CDebugDialog* dialog = new (ELeave) CDebugDialog(PDebugMain()); dialog->PrepareLC(R_PICO_DEBUG); dialog->RunLD(); @@ -348,7 +354,7 @@ void CPicolAppView::UpdateCommandList() cmd_resume->SetDimmed(dimmed); // frameskip - TInt fs_index = iCurrentConfig.iFrameskip + 1; + TInt fs_index = currentConfig.Frameskip + 1; if (fs_index >= 0 && fs_index < 10 && cmd_fs[fs_index]) { cmd_fs[fs_index]->SetChecked(ETrue); @@ -384,7 +390,6 @@ void CPicolAppUi::ConstructL() CPicolDocument::CPicolDocument(CQikApplication& aApp) : CQikDocument(aApp) { - iCurrentConfig.SetDefaults(); } CQikAppUi* CPicolDocument::CreateAppUiL() @@ -397,6 +402,7 @@ Called by the framework when ::SaveL has been called. */ void CPicolDocument::StoreL(CStreamStore& aStore, CStreamDictionary& aStreamDic) const { +#if 0 RStoreWriteStream stream; TStreamId preferenceId = stream.CreateLC(aStore); @@ -408,7 +414,7 @@ void CPicolDocument::StoreL(CStreamStore& aStore, CStreamDictionary& aStreamDic) // Ensures that any buffered data is written to aStore stream.CommitL(); CleanupStack::PopAndDestroy(); // stream - +#endif /* // tmp TInt res; @@ -427,6 +433,7 @@ Loads the application data from disk, i.e. domain data and preferences. */ void CPicolDocument::RestoreL(const CStreamStore& aStore, const CStreamDictionary& aStreamDic) { +#if 0 // Find the stream ID of the model data from the stream dictionary: TStreamId preferenceId(aStreamDic.At(KUidPicolStore)); RStoreReadStream stream; @@ -438,7 +445,7 @@ void CPicolDocument::RestoreL(const CStreamStore& aStore, const CStreamDictionar } CleanupStack::PopAndDestroy(); // stream - +#endif // tmp /* TInt res; @@ -475,9 +482,15 @@ TUid CPicolApplication::AppDllUid() const } +extern "C" TInt my_SetExceptionHandler(TInt, TExceptionHandler, TUint32); + GLDEF_C TInt E32Main() { + // doesn't work :( User::SetExceptionHandler(ExceptionHandler, (TUint32) -1); +// my_SetExceptionHandler(KCurrentThreadHandle, ExceptionHandler, 0xffffffff); + + emu_Init(); return EikStart::RunApplication(NewApplication); } diff --git a/platform/uiq3/App.h b/platform/uiq3/App.h index 9dcb572..2c1543f 100644 --- a/platform/uiq3/App.h +++ b/platform/uiq3/App.h @@ -20,10 +20,10 @@ #include #include -#include -#include +#include +#include //#include -#include +#include #include "Engine.h" #include "picodrive.hrh" @@ -41,11 +41,12 @@ const TUid KUidPicolStore = { 0x00000011 }; // store stream UID //}; +extern "C" struct _currentConfig_t; class CPicolAppView : public CQikViewBase { public: - static CPicolAppView* NewLC(CQikAppUi& aAppUi, TPicoConfig& aCurrentConfig); + static CPicolAppView* NewLC(CQikAppUi& aAppUi, TPicoConfig &aCurrentConfig); ~CPicolAppView(); // from CQikViewBase @@ -53,12 +54,12 @@ public: void HandleCommandL(CQikCommand& aCommand); void UpdateCommandList(); -protected: +protected: // from CQikViewBase void ViewConstructL(); - + private: - CPicolAppView(CQikAppUi& aAppUi, TPicoConfig& aCurrentConfig); + CPicolAppView(CQikAppUi& aAppUi, TPicoConfig &aCurrentConfig); void ConstructL(); protected: // new stuf @@ -71,7 +72,7 @@ protected: // new stuf void RunGameL();*/ private: - TPicoConfig& iCurrentConfig; + TPicoConfig &iCurrentConfig; TBool iROMLoaded; TBool iTitleAdded; }; @@ -95,7 +96,7 @@ public: void StoreL(CStreamStore& aStore, CStreamDictionary& aStreamDic) const; void RestoreL(const CStreamStore& aStore, const CStreamDictionary& aStreamDic); - TPicoConfig iCurrentConfig; + TPicoConfig iCurrentConfig; private: // from CQikDocument CQikAppUi* CreateAppUiL(); diff --git a/platform/uiq3/Dialogs.cpp b/platform/uiq3/Dialogs.cpp index 60b8685..4440bdf 100644 --- a/platform/uiq3/Dialogs.cpp +++ b/platform/uiq3/Dialogs.cpp @@ -24,9 +24,10 @@ #include // CEikHorOptionButtonList #include // CEikOptionButton #include // CEikEdwin -#include // EQuartzKeyTwoWayDown +#include // EQuartzKeyTwoWayDown -#include +#include +#include "../common/emu.h" /************************************************ @@ -35,7 +36,7 @@ * ************************************************/ -CPicoConfigDialog::CPicoConfigDialog(TPicoConfig &cfg) : config(cfg) +CPicoConfigDialog::CPicoConfigDialog(_currentConfig_t &cfg) : config(cfg) { } @@ -44,7 +45,7 @@ void CPicoConfigDialog::PostLayoutDynInitL() CEikHorOptionButtonList *buttons_rot = (CEikHorOptionButtonList*) Control( ECtlOptRotation ); CEikHorOptionButtonList *buttons_disp = (CEikHorOptionButtonList*) Control( ECtlOptScreenMode ); CEikCheckBox *chkbox_altrend= (CEikCheckBox*) Control( ECtlOptUseAltRend ); - CEikCheckBox *chkbox_acctmng= (CEikCheckBox*) Control( ECtlOptUseAccTiming ); +// CEikCheckBox *chkbox_acctmng= (CEikCheckBox*) Control( ECtlOptUseAccTiming ); CEikCheckBox *chkbox_sram = (CEikCheckBox*) Control( ECtlOptUseSRAM ); CEikCheckBox *chkbox_fps = (CEikCheckBox*) Control( ECtlOptShowFPS ); CEikCheckBox *chkbox_sound = (CEikCheckBox*) Control( ECtlOptEnableSound ); @@ -54,41 +55,59 @@ void CPicoConfigDialog::PostLayoutDynInitL() CEikChoiceListBase *combo_sndq = (CEikChoiceListBase*) Control( ECtlOptSndQuality ); CEikCheckBox *chkbox_6bpad = (CEikCheckBox*) Control( ECtlOpt6ButtonPad ); CEikCheckBox *chkbox_gzipst = (CEikCheckBox*) Control( ECtlOptGzipStates ); - CEikCheckBox *chkbox_accsprt= (CEikCheckBox*) Control( ECtlOptUseAccSprites ); +// CEikCheckBox *chkbox_accsprt= (CEikCheckBox*) Control( ECtlOptUseAccSprites ); CEikChoiceListBase *combo_region = (CEikChoiceListBase*) Control( ECtlOptRegion ); CEikOptionButton *opt_fit2 = (CEikOptionButton*) buttons_disp->ComponentControl( TPicoConfig::PMFit2 ); - - buttons_rot ->SetButtonById(ECtlOptRotation0 + config.iScreenRotation); - buttons_disp->SetButtonById(ECtlOptScreenModeCenter + config.iScreenMode); - chkbox_sram ->SetState(config.iFlags & 1 ? CEikButtonBase::ESet : CEikButtonBase::EClear); - chkbox_fps ->SetState(config.iFlags & 2 ? CEikButtonBase::ESet : CEikButtonBase::EClear); - chkbox_sound ->SetState(config.iFlags & 4 ? CEikButtonBase::ESet : CEikButtonBase::EClear); - chkbox_gzipst ->SetState(config.iFlags & 0x80 ? CEikButtonBase::ESet : CEikButtonBase::EClear); - chkbox_z80 ->SetState(config.iPicoOpt & 4 ? CEikButtonBase::ESet : CEikButtonBase::EClear); - chkbox_ym2612 ->SetState(config.iPicoOpt & 1 ? CEikButtonBase::ESet : CEikButtonBase::EClear); - chkbox_sn76496->SetState(config.iPicoOpt & 2 ? CEikButtonBase::ESet : CEikButtonBase::EClear); - chkbox_altrend->SetState(config.iPicoOpt & 0x10? CEikButtonBase::ESet : CEikButtonBase::EClear); - chkbox_6bpad ->SetState(config.iPicoOpt & 0x20? CEikButtonBase::ESet : CEikButtonBase::EClear); - chkbox_acctmng->SetState(config.iPicoOpt & 0x40? CEikButtonBase::ESet : CEikButtonBase::EClear); - chkbox_accsprt->SetState(config.iPicoOpt & 0x80? CEikButtonBase::ESet : CEikButtonBase::EClear); + CEikCheckBox *chkbox_cdda = (CEikCheckBox*) Control( ECtlOptCDcdda ); + CEikCheckBox *chkbox_pcm = (CEikCheckBox*) Control( ECtlOptCDpcm ); + CEikCheckBox *chkbox_ramcart= (CEikCheckBox*) Control( ECtlOptCDramcart ); + CEikCheckBox *chkbox_sclrot = (CEikCheckBox*) Control( ECtlOptCDscalerot ); + CEikCheckBox *chkbox_bsync = (CEikCheckBox*) Control( ECtlOptCDbettersync ); + + buttons_rot ->SetButtonById(ECtlOptRotation0 + config.rotation); + buttons_disp->SetButtonById(ECtlOptScreenModeCenter + config.scaling); + chkbox_sram ->SetState(config.EmuOpt & 1 ? CEikButtonBase::ESet : CEikButtonBase::EClear); + chkbox_fps ->SetState(config.EmuOpt & 2 ? CEikButtonBase::ESet : CEikButtonBase::EClear); + chkbox_sound ->SetState(config.EmuOpt & 4 ? CEikButtonBase::ESet : CEikButtonBase::EClear); + chkbox_gzipst ->SetState(config.EmuOpt & 8 ? CEikButtonBase::ESet : CEikButtonBase::EClear); + chkbox_z80 ->SetState(config.s_PicoOpt& 4 ? CEikButtonBase::ESet : CEikButtonBase::EClear); + chkbox_ym2612 ->SetState(config.s_PicoOpt& 1 ? CEikButtonBase::ESet : CEikButtonBase::EClear); + chkbox_sn76496->SetState(config.s_PicoOpt& 2 ? CEikButtonBase::ESet : CEikButtonBase::EClear); + chkbox_altrend->SetState(config.s_PicoOpt& 0x10? CEikButtonBase::ESet : CEikButtonBase::EClear); + chkbox_6bpad ->SetState(config.s_PicoOpt& 0x20? CEikButtonBase::ESet : CEikButtonBase::EClear); +// chkbox_acctmng->SetState(config.s_PicoOpt& 0x40? CEikButtonBase::ESet : CEikButtonBase::EClear); +// chkbox_accsprt->SetState(config.s_PicoOpt& 0x80? CEikButtonBase::ESet : CEikButtonBase::EClear); + chkbox_cdda ->SetState(config.s_PicoOpt&0x0800?CEikButtonBase::ESet : CEikButtonBase::EClear); + chkbox_pcm ->SetState(config.s_PicoOpt&0x0400?CEikButtonBase::ESet : CEikButtonBase::EClear); + chkbox_ramcart->SetState(config.s_PicoOpt&0x8000?CEikButtonBase::ESet : CEikButtonBase::EClear); + chkbox_sclrot ->SetState(config.s_PicoOpt&0x1000?CEikButtonBase::ESet : CEikButtonBase::EClear); + chkbox_bsync ->SetState(config.s_PicoOpt&0x2000?CEikButtonBase::ESet : CEikButtonBase::EClear); // dim "fit2" if we are not in 0 or 180 mode - if(config.iScreenRotation != TPicoConfig::PRot0 && config.iScreenRotation != TPicoConfig::PRot180) opt_fit2->SetDimmed(ETrue); + if (config.rotation != TPicoConfig::PRot0 && config.rotation != TPicoConfig::PRot180) + opt_fit2->SetDimmed(ETrue); // dim some stuff for alternative renderer - if(config.iPicoOpt & 0x10) { + if (config.s_PicoOpt & 0x10) { // dim accurate sprites - chkbox_accsprt->SetState(CEikButtonBase::EClear); - chkbox_accsprt->SetDimmed(ETrue); + //chkbox_accsprt->SetState(CEikButtonBase::EClear); + //chkbox_accsprt->SetDimmed(ETrue); // dim fit if(buttons_rot->LabeledButtonId() == ECtlOptRotation0 || buttons_rot->LabeledButtonId() == ECtlOptRotation180) ((CEikOptionButton*)(buttons_disp->ComponentControl(TPicoConfig::PMFit)))->SetDimmed(ETrue); } - TInt sel = (config.iPicoOpt&8) ? 5 : 0; - sel+= (config.iFlags>>3)&7; + TInt sel = 0; + switch (config.s_PsndRate) { + case 11025: sel = 1; break; + case 16000: sel = 2; break; + case 22050: sel = 3; break; + case 44100: sel = 4; break; + } + sel += (config.s_PicoOpt&8) ? 5 : 0; if (sel >= 10) sel = 0; combo_sndq->SetCurrentItem(sel); - switch(config.PicoRegion) { + + switch(config.s_PicoRegion) { case 1: sel = 4; break; case 2: sel = 3; break; case 4: sel = 2; break; @@ -105,7 +124,7 @@ TBool CPicoConfigDialog::OkToExitL(TInt aButtonId) CEikHorOptionButtonList *buttons_rot = (CEikHorOptionButtonList*) Control( ECtlOptRotation ); CEikHorOptionButtonList *buttons_disp = (CEikHorOptionButtonList*) Control( ECtlOptScreenMode ); CEikCheckBox *chkbox_altrend= (CEikCheckBox*) Control( ECtlOptUseAltRend ); - CEikCheckBox *chkbox_acctmng= (CEikCheckBox*) Control( ECtlOptUseAccTiming ); +// CEikCheckBox *chkbox_acctmng= (CEikCheckBox*) Control( ECtlOptUseAccTiming ); CEikCheckBox *chkbox_sram = (CEikCheckBox*) Control( ECtlOptUseSRAM ); CEikCheckBox *chkbox_fps = (CEikCheckBox*) Control( ECtlOptShowFPS ); CEikCheckBox *chkbox_sound = (CEikCheckBox*) Control( ECtlOptEnableSound ); @@ -115,35 +134,50 @@ TBool CPicoConfigDialog::OkToExitL(TInt aButtonId) CEikChoiceListBase *combo_sndq = (CEikChoiceListBase*) Control( ECtlOptSndQuality ); CEikCheckBox *chkbox_6bpad = (CEikCheckBox*) Control( ECtlOpt6ButtonPad ); CEikCheckBox *chkbox_gzipst = (CEikCheckBox*) Control( ECtlOptGzipStates ); - CEikCheckBox *chkbox_accsprt= (CEikCheckBox*) Control( ECtlOptUseAccSprites ); +// CEikCheckBox *chkbox_accsprt= (CEikCheckBox*) Control( ECtlOptUseAccSprites ); CEikChoiceListBase *combo_region = (CEikChoiceListBase*) Control( ECtlOptRegion ); - - config.iScreenRotation = (TPicoConfig::TPicoScreenRotation) (buttons_rot->LabeledButtonId() - ECtlOptRotation0); - config.iScreenMode = (TPicoConfig::TPicoScreenMode) (buttons_disp->LabeledButtonId() - ECtlOptScreenModeCenter); - - if(chkbox_sram ->State() == CEikButtonBase::ESet) config.iFlags |= 1; else config.iFlags &= ~1; - if(chkbox_fps ->State() == CEikButtonBase::ESet) config.iFlags |= 2; else config.iFlags &= ~2; - if(chkbox_sound ->State() == CEikButtonBase::ESet) config.iFlags |= 4; else config.iFlags &= ~4; - if(chkbox_gzipst ->State() == CEikButtonBase::ESet) config.iFlags |= 0x80; else config.iFlags &= ~0x80; - if(chkbox_z80 ->State() == CEikButtonBase::ESet) config.iPicoOpt |= 4; else config.iPicoOpt &= ~4; - if(chkbox_ym2612 ->State() == CEikButtonBase::ESet) config.iPicoOpt |= 1; else config.iPicoOpt &= ~1; - if(chkbox_sn76496->State() == CEikButtonBase::ESet) config.iPicoOpt |= 2; else config.iPicoOpt &= ~2; - if(chkbox_altrend->State() == CEikButtonBase::ESet) config.iPicoOpt |= 0x10;else config.iPicoOpt &= ~0x10; - if(chkbox_6bpad ->State() == CEikButtonBase::ESet) config.iPicoOpt |= 0x20;else config.iPicoOpt &= ~0x20; - if(chkbox_acctmng->State() == CEikButtonBase::ESet) config.iPicoOpt |= 0x40;else config.iPicoOpt &= ~0x40; - if(chkbox_accsprt->State() == CEikButtonBase::ESet) config.iPicoOpt |= 0x80;else config.iPicoOpt &= ~0x80; + CEikCheckBox *chkbox_cdda = (CEikCheckBox*) Control( ECtlOptCDcdda ); + CEikCheckBox *chkbox_pcm = (CEikCheckBox*) Control( ECtlOptCDpcm ); + CEikCheckBox *chkbox_ramcart= (CEikCheckBox*) Control( ECtlOptCDramcart ); + CEikCheckBox *chkbox_sclrot = (CEikCheckBox*) Control( ECtlOptCDscalerot ); + CEikCheckBox *chkbox_bsync = (CEikCheckBox*) Control( ECtlOptCDbettersync ); + + config.rotation = (TPicoConfig::TPicoScreenRotation) (buttons_rot->LabeledButtonId() - ECtlOptRotation0); + config.scaling = (TPicoConfig::TPicoScreenMode) (buttons_disp->LabeledButtonId() - ECtlOptScreenModeCenter); + + if(chkbox_sram ->State() == CEikButtonBase::ESet) config.EmuOpt |= 1; else config.EmuOpt &= ~1; + if(chkbox_fps ->State() == CEikButtonBase::ESet) config.EmuOpt |= 2; else config.EmuOpt &= ~2; + if(chkbox_sound ->State() == CEikButtonBase::ESet) config.EmuOpt |= 4; else config.EmuOpt &= ~4; + if(chkbox_gzipst ->State() == CEikButtonBase::ESet) config.EmuOpt |= 8; else config.EmuOpt &= ~8; + if(chkbox_z80 ->State() == CEikButtonBase::ESet) config.s_PicoOpt|= 4; else config.s_PicoOpt&= ~4; + if(chkbox_ym2612 ->State() == CEikButtonBase::ESet) config.s_PicoOpt|= 1; else config.s_PicoOpt&= ~1; + if(chkbox_sn76496->State() == CEikButtonBase::ESet) config.s_PicoOpt|= 2; else config.s_PicoOpt&= ~2; + if(chkbox_altrend->State() == CEikButtonBase::ESet) config.s_PicoOpt|= 0x10;else config.s_PicoOpt&= ~0x10; + if(chkbox_6bpad ->State() == CEikButtonBase::ESet) config.s_PicoOpt|= 0x20;else config.s_PicoOpt&= ~0x20; +// if(chkbox_acctmng->State() == CEikButtonBase::ESet) config.s_PicoOpt|= 0x40;else config.s_PicoOpt&= ~0x40; +// if(chkbox_accsprt->State() == CEikButtonBase::ESet) config.s_PicoOpt|= 0x80;else config.s_PicoOpt&= ~0x80; + if(chkbox_cdda ->State() == CEikButtonBase::ESet) config.s_PicoOpt |= 0x0800; else config.s_PicoOpt&= ~0x0800; + if(chkbox_pcm ->State() == CEikButtonBase::ESet) config.s_PicoOpt |= 0x0400; else config.s_PicoOpt&= ~0x0400; + if(chkbox_ramcart->State() == CEikButtonBase::ESet) config.s_PicoOpt |= 0x8000; else config.s_PicoOpt&= ~0x8000; + if(chkbox_sclrot ->State() == CEikButtonBase::ESet) config.s_PicoOpt |= 0x1000; else config.s_PicoOpt&= ~0x1000; + if(chkbox_bsync ->State() == CEikButtonBase::ESet) config.s_PicoOpt |= 0x2000; else config.s_PicoOpt&= ~0x2000; TInt sel = combo_sndq->CurrentItem(); - if(sel >= 5) { config.iPicoOpt |= 8; sel-=5; } else config.iPicoOpt &= ~8; - config.iFlags &= ~0x38; - config.iFlags |= (sel<<3)&0x38; - - switch(combo_region->CurrentItem()) { - case 4: config.PicoRegion = 1; break; - case 3: config.PicoRegion = 2; break; - case 2: config.PicoRegion = 4; break; - case 1: config.PicoRegion = 8; break; - default:config.PicoRegion = 0; // auto + if(sel >= 5) { config.s_PicoOpt |= 8; sel-=5; } else config.s_PicoOpt &= ~8; + switch (sel) { + default:config.s_PsndRate = 8000; break; + case 1: config.s_PsndRate = 11025; break; + case 2: config.s_PsndRate = 16000; break; + case 3: config.s_PsndRate = 22050; break; + case 4: config.s_PsndRate = 44100; break; + } + + switch (combo_region->CurrentItem()) { + case 4: config.s_PicoRegion = 1; break; + case 3: config.s_PicoRegion = 2; break; + case 2: config.s_PicoRegion = 4; break; + case 1: config.s_PicoRegion = 8; break; + default:config.s_PicoRegion = 0; // auto } return ETrue; @@ -152,7 +186,8 @@ TBool CPicoConfigDialog::OkToExitL(TInt aButtonId) // simple GUI stuff needs lots of code void CPicoConfigDialog::HandleControlStateChangeL(TInt aControlId) { - if(aControlId == ECtlOptEnableSound) { + if (aControlId == ECtlOptEnableSound) + { CEikCheckBox *chkbox_sound = (CEikCheckBox*) Control( ECtlOptEnableSound ); CEikCheckBox *chkbox_z80 = (CEikCheckBox*) Control( ECtlOptEmulateZ80 ); CEikCheckBox *chkbox_ym2612 = (CEikCheckBox*) Control( ECtlOptEmulateYM2612 ); @@ -179,9 +214,11 @@ void CPicoConfigDialog::HandleControlStateChangeL(TInt aControlId) chkbox_sn76496->DrawDeferred(); } } - } else if(aControlId == ECtlOptUseAltRend || aControlId == ECtlOptRotation) { + } + else if(aControlId == ECtlOptUseAltRend || aControlId == ECtlOptRotation) + { CEikCheckBox *chkbox_altrend= (CEikCheckBox*) Control( ECtlOptUseAltRend ); - CEikCheckBox *chkbox_accsprt= (CEikCheckBox*) Control( ECtlOptUseAccSprites ); +// CEikCheckBox *chkbox_accsprt= (CEikCheckBox*) Control( ECtlOptUseAccSprites ); CEikHorOptionButtonList *buttons_rot = (CEikHorOptionButtonList*) Control( ECtlOptRotation ); CEikHorOptionButtonList *buttons_disp = (CEikHorOptionButtonList*) Control( ECtlOptScreenMode ); CEikOptionButton *opt_fit = (CEikOptionButton*) buttons_disp->ComponentControl( TPicoConfig::PMFit ); @@ -189,14 +226,14 @@ void CPicoConfigDialog::HandleControlStateChangeL(TInt aControlId) TBool dimmed = chkbox_altrend->State() == CEikButtonBase::ESet; // show/hide more stuff for alternative renderer - chkbox_accsprt->SetDimmed(dimmed); +// chkbox_accsprt->SetDimmed(dimmed); if(buttons_rot->LabeledButtonId() == ECtlOptRotation0 || buttons_rot->LabeledButtonId() == ECtlOptRotation180) { opt_fit->SetDimmed(dimmed); if(dimmed && buttons_disp->LabeledButtonId() == ECtlOptScreenModeFit) buttons_disp->SetButtonById(ECtlOptScreenModeFit2); } else opt_fit->SetDimmed(EFalse); - chkbox_accsprt->DrawDeferred(); +// chkbox_accsprt->DrawDeferred(); buttons_disp->DrawDeferred(); if(buttons_rot->LabeledButtonId() == ECtlOptRotation0 || buttons_rot->LabeledButtonId() == ECtlOptRotation180) { diff --git a/platform/uiq3/Dialogs.h b/platform/uiq3/Dialogs.h index 7184f94..cd865ec 100644 --- a/platform/uiq3/Dialogs.h +++ b/platform/uiq3/Dialogs.h @@ -25,7 +25,7 @@ #include #include -#include +#include /************************************************ @@ -34,19 +34,19 @@ * ************************************************/ -class TPicoConfig; +extern "C" struct _currentConfig_t; class CPicoConfigDialog : public CEikDialog { public: - CPicoConfigDialog(TPicoConfig &cfg); + CPicoConfigDialog(_currentConfig_t &cfg); protected: // framework void PostLayoutDynInitL(); void HandleControlStateChangeL(TInt aControlId); TBool OkToExitL(TInt aButtonId); - TPicoConfig &config; + _currentConfig_t &config; }; diff --git a/platform/uiq3/Engine.cpp b/platform/uiq3/Engine.cpp index 5e21b91..ee9b6a5 100644 --- a/platform/uiq3/Engine.cpp +++ b/platform/uiq3/Engine.cpp @@ -23,46 +23,48 @@ #include #include "version.h" -#include "../../pico/picoInt.h" +#include +#include "../common/emu.h" #include "engine/debug.h" -#include "app.h" +#include "App.h" // this is where we start to break a bunch of symbian rules extern TInt machineUid; extern int gamestate, gamestate_next; -extern TPicoConfig *currentConfig; +extern char *loadrom_fname; +extern int loadrom_result; extern const char *actionNames[]; -RSemaphore pauseSemaphore; RSemaphore initSemaphore; -const char *RomFileName = 0; +RSemaphore pauseSemaphore; +RSemaphore loadWaitSemaphore; int pico_was_reset = 0; -unsigned char *rom_data = 0; static CPicolAppView *appView = 0; TInt CPicoGameSession::Do(const TPicoServRqst what, TAny *param) { - switch (what) { + switch (what) + { case PicoMsgLoadState: - if(!rom_data) return -1; // no ROM - return saveLoadGame(1); + if(!rom_loaded) return -1; // no ROM + return emu_SaveLoadGame(1, 0); case PicoMsgSaveState: - if(!rom_data) return -1; - return saveLoadGame(0); + if(!rom_loaded) return -1; + return emu_SaveLoadGame(0, 0); case PicoMsgLoadROM: return loadROM((TPtrC16 *)param); case PicoMsgResume: - DEBUGPRINT(_L("resume with rom %08x"), rom_data); - if(rom_data) { + DEBUGPRINT(_L("resume")); + if(rom_loaded) { return ChangeRunState(PGS_Running); } return 1; case PicoMsgReset: - if(rom_data) { + if(rom_loaded) { PicoReset(); pico_was_reset = 1; return ChangeRunState(PGS_Running); @@ -104,6 +106,8 @@ TInt CPicoGameSession::StartEmuThread() initSemaphore.CreateLocal(0); if (pauseSemaphore.Handle() <= 0) pauseSemaphore.CreateLocal(0); + if (loadWaitSemaphore.Handle() <= 0) + loadWaitSemaphore.CreateLocal(0); RThread thread; if(iThreadWatcher && (res = thread.Open(iThreadWatcher->iTid)) == KErrNone) { @@ -118,7 +122,6 @@ TInt CPicoGameSession::StartEmuThread() thread.Close(); } - //semaphore.CreateLocal(0); // create a semaphore so we know when thread init is finished res=thread.Create(_L("PicoEmuThread"), // create new server thread EmuThreadFunction, // thread's main function KDefaultStackSize, @@ -134,7 +137,7 @@ TInt CPicoGameSession::StartEmuThread() iThreadWatcher = CThreadWatcher::NewL(thread.Id()); thread.Resume(); // start it going DEBUGPRINT(_L("initSemaphore.Wait()")); - res = initSemaphore.Wait(1000*1000); // wait until it's initialized + res = initSemaphore.Wait(3*1000*1000); // wait until it's initialized DEBUGPRINT(_L("initSemaphore resume, ExitReason() == %i"), thread.ExitReason()); res |= thread.ExitReason(); thread.Close(); // we're no longer interested in the other thread @@ -164,96 +167,34 @@ TInt CPicoGameSession::ChangeRunState(TPicoGameState newstate, TPicoGameState ne TInt CPicoGameSession::loadROM(TPtrC16 *pptr) { - TInt res, i; - char buff[0x31]; + TInt ret; + char buff[150]; - if(rom_data) { - // save SRAM for previous ROM - if(currentConfig->iFlags & 1) - saveLoadGame(0, 1); - } - - RomFileName = 0; - if(rom_data) { - free(rom_data); - rom_data = 0; - } + // make sure emu thread is ok + ret = ChangeRunState(PGS_Paused); + if(ret) return ret; // read the contents of the client pointer into a TPtr. static TBuf8 writeBuf; writeBuf.Copy(*pptr); - // detect wrong extensions (.srm and .mds) - TBuf8<5> ext; - ext.Copy(writeBuf.Right(4)); - ext.LowerCase(); - if(!strcmp((char *)ext.PtrZ(), ".srm") || !strcmp((char *)ext.PtrZ(), "s.gz") || // .mds.gz - !strcmp((char *)ext.PtrZ(), ".mds")) { - return PicoErrNotRom; - } - - FILE *rom = fopen((char *) writeBuf.PtrZ(), "rb"); - if(!rom) { - DEBUGPRINT(_L("failed to open rom.")); - return PicoErrRomOpenFailed; - } - - // make sure emu thread is ok - res = ChangeRunState(PGS_Paused); - if(res) { - fclose(rom); - return res; - } + // push the emu thead to a load state. This is done so that it owns all file handles. + // If successful, in will enter PGS_Running state by itself. + loadrom_fname = (char *)writeBuf.PtrZ(); + loadrom_result = 0; + ret = ChangeRunState(PGS_ReloadRom); + if(ret) return ret; - unsigned int rom_size = 0; - // zipfile support - if(!strcmp((char *)ext.PtrZ(), ".zip")) { - fclose(rom); - res = CartLoadZip((const char *) writeBuf.PtrZ(), &rom_data, &rom_size); - if(res) { - DEBUGPRINT(_L("CartLoadZip() failed (%i)"), res); - return res; - } - } else { - if( (res = PicoCartLoad(rom, &rom_data, &rom_size)) ) { - DEBUGPRINT(_L("PicoCartLoad() failed (%i)"), res); - fclose(rom); - return PicoErrOutOfMem; - } - fclose(rom); - } + loadWaitSemaphore.Wait(20*1000*1000); - // detect wrong files (Pico crashes on very small files), also see if ROM EP is good - if(rom_size <= 0x200 || strncmp((char *)rom_data, "Pico", 4) == 0 || - ((*(TUint16 *)(rom_data+4)<<16)|(*(TUint16 *)(rom_data+6))) >= (int)rom_size) { - free(rom_data); - rom_data = 0; + if (loadrom_result == 0) return PicoErrNotRom; - } - DEBUGPRINT(_L("PicoCartInsert(0x%08X, %d);"), rom_data, rom_size); - if(PicoCartInsert(rom_data, rom_size)) { - return PicoErrOutOfMem; - } - - pico_was_reset = 1; - - // global ROM file name for later use - RomFileName = (const char *) writeBuf.PtrZ(); - - // name from the ROM itself - for(i = 0; i < 0x30; i++) - buff[i] = rom_data[0x150 + (i^1)]; // unbyteswap - for(buff[i] = 0, i--; i >= 0; i--) { - if(buff[i] != ' ') break; - buff[i] = 0; - } + emu_getGameName(buff); TPtrC8 buff8((TUint8*) buff); iRomInternalName.Copy(buff8); - // load SRAM for this ROM - if(currentConfig->iFlags & 1) - saveLoadGame(1, 1); + DEBUGPRINT(_L("done waiting for ROM load")); // debug #ifdef __DEBUG_PRINT @@ -262,8 +203,6 @@ TInt CPicoGameSession::loadROM(TPtrC16 *pptr) User::AllocSize(mem); DEBUGPRINT(_L("comm: cels=%d, size=%d KB"), cells, mem/1024); ChangeRunState(PGS_DebugHeap, PGS_Running); - #else - ChangeRunState(PGS_Running); #endif return 0; @@ -272,23 +211,9 @@ TInt CPicoGameSession::loadROM(TPtrC16 *pptr) TInt CPicoGameSession::changeConfig(TPicoConfig *aConfig) { - DEBUGPRINT(_L("got new config.")); - - currentConfig = aConfig; - - // set PicoOpt and rate - PicoRegionOverride = currentConfig->PicoRegion; - PicoOpt = currentConfig->iPicoOpt; - switch((currentConfig->iFlags>>3)&7) { - case 1: PsndRate=11025; break; - case 2: PsndRate=16000; break; - case 3: PsndRate=22050; break; - case 4: PsndRate=44100; break; - default: PsndRate= 8000; break; - } - // 6 button pad, enable XYZM config if needed - if(PicoOpt & 0x20) { + if (PicoOpt & POPT_6BTN_PAD) + { actionNames[8] = "Z"; actionNames[9] = "Y"; actionNames[10] = "X"; @@ -298,16 +223,15 @@ TInt CPicoGameSession::changeConfig(TPicoConfig *aConfig) } // if we are in center 90||270 modes, we can bind renderer switcher - if(currentConfig->iScreenMode == TPicoConfig::PMFit && - (currentConfig->iScreenRotation == TPicoConfig::PRot0 || currentConfig->iScreenRotation == TPicoConfig::PRot180)) - actionNames[25] = 0; - else actionNames[25] = "RENDERER"; + if (currentConfig.scaling == TPicoConfig::PMFit && + (currentConfig.rotation == TPicoConfig::PRot0 || currentConfig.rotation == TPicoConfig::PRot180)) + actionNames[25] = 0; + else actionNames[25] = "RENDERER"; return 0; } -void MainOldCleanup(); // from main.cpp #ifdef __DEBUG_PRINT_FILE extern RMutex logMutex; #endif @@ -346,7 +270,7 @@ void CPicoGameSession::freeResources() } - if(iThreadWatcher != NULL) + if (iThreadWatcher != NULL) { DEBUGPRINT(_L("delete iThreadWatcher")); delete iThreadWatcher; @@ -354,12 +278,13 @@ void CPicoGameSession::freeResources() iThreadWatcher = NULL; } - MainOldCleanup(); - if (initSemaphore.Handle() > 0) initSemaphore.Close(); if (pauseSemaphore.Handle() > 0) pauseSemaphore.Close(); + if (loadWaitSemaphore.Handle() > 0) + loadWaitSemaphore.Close(); + DEBUGPRINT(_L("freeResources() returning")); #ifdef __DEBUG_PRINT_FILE if (logMutex.Handle() > 0) logMutex.Close(); @@ -368,67 +293,7 @@ void CPicoGameSession::freeResources() TBool CPicoGameSession::iEmuRunning = EFalse; CThreadWatcher *CPicoGameSession::iThreadWatcher = 0; -TBuf<0x30> CPicoGameSession::iRomInternalName; - - -void TPicoConfig::SetDefaults() -{ - iLastROMFile.SetLength(0); - iScreenRotation = PRot270; - iScreenMode = PMCenter; - iFlags = 1; // use_sram - iPicoOpt = 0; // all off - iFrameskip = PFSkipAuto; - - Mem::FillZ(iKeyBinds, sizeof(iKeyBinds)); - Mem::FillZ(iAreaBinds, sizeof(iAreaBinds)); - iKeyBinds[0xd5] = 1<<26; // bind back -} - -// load config -void TPicoConfig::InternalizeL(RReadStream &aStream) -{ - TInt32 version, fname_len; - version = aStream.ReadInt32L(); - fname_len = aStream.ReadInt32L(); - - // not sure if this is safe - iLastROMFile.SetMax(); - aStream.ReadL((TUint8 *) iLastROMFile.Ptr(), KMaxFileName*2); - iLastROMFile.SetLength(fname_len); - - iScreenRotation = aStream.ReadInt32L(); - iScreenMode = aStream.ReadInt32L(); - iFlags = aStream.ReadUint32L(); - iPicoOpt = aStream.ReadInt32L(); - iFrameskip = aStream.ReadInt32L(); - - aStream.ReadL((TUint8 *)iKeyBinds, sizeof(iKeyBinds)); - aStream.ReadL((TUint8 *)iAreaBinds, sizeof(iAreaBinds)); - - PicoRegion = aStream.ReadInt32L(); -} - -// save config -void TPicoConfig::ExternalizeL(RWriteStream &aStream) const -{ - TInt version = (KPicoMajorVersionNumber<<24)+(KPicoMinorVersionNumber<<16); - - aStream.WriteInt32L(version); - aStream.WriteInt32L(iLastROMFile.Length()); - aStream.WriteL((const TUint8 *)iLastROMFile.Ptr(), KMaxFileName*2); - - aStream.WriteInt32L(iScreenRotation); - aStream.WriteInt32L(iScreenMode); - aStream.WriteUint32L(iFlags); - aStream.WriteInt32L(iPicoOpt); - aStream.WriteInt32L(iFrameskip); - - aStream.WriteL((const TUint8 *)iKeyBinds, sizeof(iKeyBinds)); - aStream.WriteL((const TUint8 *)iAreaBinds, sizeof(iAreaBinds)); - - aStream.WriteInt32L(PicoRegion); -} +TBuf<150> CPicoGameSession::iRomInternalName; // CThreadWatcher @@ -482,3 +347,10 @@ void CThreadWatcher::DoCancel() thread.Close(); } } + +extern "C" void cache_flush_d_inval_i(const void *start_addr, const void *end_addr) +{ + // TODO + User::IMB_Range((TAny *)start_addr, (TAny *)end_addr); +} + diff --git a/platform/uiq3/Engine.h b/platform/uiq3/Engine.h index 19bd84e..b81b6af 100644 --- a/platform/uiq3/Engine.h +++ b/platform/uiq3/Engine.h @@ -28,6 +28,7 @@ enum TPicoGameState { PGS_Quit, PGS_KeyConfig, PGS_DebugHeap, + PGS_ReloadRom, }; enum TPicoServRqst { @@ -81,9 +82,9 @@ struct TPicoKeyConfigEntry class TPicoConfig { public: - void SetDefaults(); - void InternalizeL(RReadStream &aStream); - void ExternalizeL(RWriteStream &aStream) const; +// void SetDefaults(); +// void InternalizeL(RReadStream &aStream); +// void ExternalizeL(RWriteStream &aStream) const; enum TPicoScreenRotation { PRot0, @@ -102,18 +103,7 @@ public: }; public: - TFileName iLastROMFile; - - TInt32 iScreenRotation; - TInt32 iScreenMode; - TUint32 iFlags; // LSb->MSb: use_sram, show_fps, enable_sound, sound_rate(3bits), gzip_saves{=0x40}, dont_use_mot_vol - // enable_ym2612&dac, enable_sn76496, enable_z80, stereo_sound; - // alt_renderer, 6button_gamepad, accurate_timing - TInt32 iPicoOpt; - TInt32 iFrameskip; - TUint32 iKeyBinds[256]; // a binding for every keycode - TUint32 iAreaBinds[19]; - TInt32 PicoRegion; + TFileName iLastROMFile; // used as tmp only }; @@ -141,7 +131,7 @@ public: static void freeResources(); static TBool iEmuRunning; - static TBuf<0x30> iRomInternalName; + static TBuf<150> iRomInternalName; private: // services available @@ -153,7 +143,4 @@ private: static CThreadWatcher *iThreadWatcher; }; -// global -int saveLoadGame(int load, int sram=0); - #endif diff --git a/platform/uiq3/Makefile b/platform/uiq3/Makefile index fa14569..9e91127 100644 --- a/platform/uiq3/Makefile +++ b/platform/uiq3/Makefile @@ -1,307 +1,146 @@ -# makefile for GCCE +# makefile for uiq3_patcher_0_2.tar.gz +export CROSS = arm-none-symbianelf- +APPNAME = PicoDrive +VER_MAJ = 1 +VER_MIN = 51 +VENDOR = notaz +UID3 = A00010F3 +EPOCROOT = /opt/uiq3/ +EPOCLIBS = qikdlg.lib etext.lib bafl.lib efsrv.lib eikctl.lib ws32.lib \ + eikdlg.lib gdi.lib estor.lib hal.lib mediaclient.lib mediaclientaudiostream.lib +STACK = 0x3000 +HEAP = 0x10,0x1000000 + # settings -#dprint = 1 asm_memory = 1 asm_render = 1 +asm_ym2612 = 1 +asm_misc = 1 +asm_cdpico = 1 +asm_cdmemory = 1 asm_blit = 1 +use_cyclone = 1 #use_musashi = 1 -#up = 1 -#sis = 1 - -# targets -all: $(EPOCROOT2)epoc32 MAKEDIRS RESOURCES PicoDrive.exe - -clean : - @perl -S ermdir.pl _build - @erase 2>>nul rsc\*.rsc - @erase 2>>nul rsc\*.rsg - @erase 2>>nul rsc\PicoDrive.mb? - - -# paths -$(EPOCROOT2)epoc32 : - @echo Please set EPOCROOT2 environmental variable to full path to your SDK - @echo with ending slash (something like C:\Uiq_21\) - @cd : 2> NUL # do something stupid to make it silently fail - -# resource compiler hates drive lettered paths -EPOCROOT2_NODRV = $(filter \\%,$(subst :, ,$(EPOCROOT2))) -EPOCLIB = $(EPOCROOT2)EPOC32\RELEASE\ARMV5 - -# C/C++ Compiler -CC=arm-none-symbianelf-gcc - -# Linker -LD=arm-none-symbianelf-ld - -# Assembler -ASM=arm-none-symbianelf-as - -# Archiver -AR=arm-none-symbianelf-ar - -# Strip -STRIP=arm-none-symbianelf-strip - -# gcc config -GCCDEFINES = -DNDEBUG -D_UNICODE -D__GCCE__ -D__SYMBIAN32__ -D__EPOC32__ -D__MARM__ \ - -D__EABI__ -D__MARM_ARMV5__ -D__EXE__ -D__SUPPORT_CPP_EXCEPTIONS__ \ - -D__MARM_ARMV5__ -D__PRODUCT_INCLUDE__=\"$(EPOCROOT2)epoc32/include/variant/UIQ_3.0.hrh\" - -GCCDEFINES += -D_UNZIP_SUPPORT -D__BROKEN_FWRITE - -# 'CSL Arm Toolchain' stuff must be specified after Symbian includes -GCCINCLUDES = -I "$(EPOCROOT2)epoc32\include\variant" -I "$(EPOCROOT2)EPOC32\INCLUDE" -I "$(EPOCROOT2)EPOC32\INCLUDE\LIBC" \ - -I "$(EPOCROOT2)\CSL Arm Toolchain\lib\gcc\arm-none-symbianelf\3.4.3\include" -I. - -# -funit-at-a-time is not compatible with SDK, it either has linker problems or does not start on device -GCCCOMMFLAGS = -Wall -Wno-unknown-pragmas -fexceptions -march=armv5t -mapcs -pipe -nostdinc -msoft-float \ - $(GCCINCLUDES) -include "$(EPOCROOT2)EPOC32/INCLUDE/GCCE/GCCE.h" -marm - -GCCCPPFLAGS = -x c++ -Wno-ctor-dtor-privacy -O3 -fno-unit-at-a-time -GCCCFLAGS = -x c -O3 -fno-unit-at-a-time - -GCCLDFLAGS = -L"$(EPOCROOT2)CSL Arm Toolchain\arm-none-symbianelf\lib" \ - -L"$(EPOCROOT2)CSL Arm Toolchain\lib\gcc\arm-none-symbianelf\3.4.3" \ - --target1-abs --no-undefined -nostdlib -shared -Ttext 0x8000 -Tdata 0x400000 --default-symver - -# libs -LIBS = \ - $(EPOCLIB)\LIB\ESTLIB.dso \ - $(EPOCLIB)\urel\qikalloc.lib \ - $(EPOCLIB)\LIB\euser.dso \ - $(EPOCLIB)\LIB\apparc.dso \ - $(EPOCLIB)\LIB\cone.dso \ - $(EPOCLIB)\LIB\eikcore.dso \ - $(EPOCLIB)\LIB\eikcoctl.dso \ - $(EPOCLIB)\LIB\qikcore.dso \ - $(EPOCLIB)\LIB\qikdlg.dso \ - $(EPOCLIB)\LIB\etext.dso \ - $(EPOCLIB)\LIB\bafl.dso \ - $(EPOCLIB)\LIB\efsrv.dso \ - $(EPOCLIB)\LIB\eikctl.dso \ - $(EPOCLIB)\LIB\WS32.dso \ - $(EPOCLIB)\LIB\EIKDLG.dso \ - $(EPOCLIB)\LIB\GDI.dso \ - $(EPOCLIB)\LIB\estor.dso \ - $(EPOCLIB)\LIB\EZLIB.dso \ - $(EPOCLIB)\LIB\HAL.dso \ - $(EPOCLIB)\LIB\mediaclient.dso \ - $(EPOCLIB)\LIB\mediaclientaudiostream.dso - -LIBS += \ - $(EPOCLIB)\LIB\qikallocdll.dso \ - $(EPOCLIB)\UREL\usrt2_2.lib \ - $(EPOCLIB)\LIB\dfpaeabi.dso \ - $(EPOCLIB)\LIB\dfprvct2_2.dso \ - $(EPOCLIB)\LIB\drtaeabi.dso \ - $(EPOCLIB)\LIB\scppnwdl.dso \ - $(EPOCLIB)\LIB\drtrvct2_2.dso # objects # launcher -OBJECTS += _build\App.o _build\Engine.o _build\Dialogs.o _build\CSimpleTextParser.o +OBJ += App.o Engine.o Dialogs.o CSimpleTextParser.o emu.o # engine -OBJECTS += _build\main.o _build\vid.o _build\polledas.o _build\audio_mediaserver.o _build\debug.o - +OBJ += engine/main.o engine/vid.o engine/polledas.o engine/audio_mediaserver.o engine/debug.o +ifeq "$(asm_blit)" "1" +OBJ += engine/blit_asm.o +else +OBJ += engine/blit.o +endif +# common +OBJ += ../common/emu.o ../common/config.o ../common/menu.o ../common/mp3_helix.o # Pico -OBJECTS += _build\Area.o _build\Cart.o _build\Utils.o _build\Memory.o _build\Misc.o \ - _build\Pico.o _build\Sek.o _build\VideoPort.o _build\Draw2.o _build\Draw.o +OBJ += Pico/Area.o Pico/Cart.o Pico/Memory.o Pico/Misc.o Pico/Pico.o Pico/Sek.o \ + Pico/VideoPort.o Pico/Draw2.o Pico/Draw.o Pico/Patch.o Pico/Debug.o +# Pico - CD +OBJ += Pico/cd/Pico.o Pico/cd/Memory.o Pico/cd/Sek.o Pico/cd/LC89510.o \ + Pico/cd/cd_sys.o Pico/cd/cd_file.o Pico/cd/cue.o Pico/cd/gfx_cd.o \ + Pico/cd/Area.o Pico/cd/Misc.o Pico/cd/pcm.o Pico/cd/buffering.o +# Pico - Pico +OBJ += Pico/Pico/Pico.o Pico/Pico/Memory.o Pico/Pico/xpcm.o +# Pico - carthw +OBJ += Pico/carthw/carthw.o Pico/carthw/svp/svp.o Pico/carthw/svp/Memory.o \ + Pico/carthw/svp/ssp16.o Pico/carthw/svp/compiler.o Pico/carthw/svp/stub_arm.o + # asm stuff ifeq "$(asm_render)" "1" -GCCDEFINES += -D_ASM_DRAW_C -OBJECTS += _build\draw_asm.o _build\draw2_asm.o +CFLAGS += -D_ASM_DRAW_C +OBJ += Pico/draw_asm.o Pico/draw2_asm.o endif ifeq "$(asm_memory)" "1" -GCCDEFINES += -D_ASM_MEMORY_C -OBJECTS += _build\memory_asm.o +CFLAGS += -D_ASM_MEMORY_C +OBJ += Pico/memory_asm.o endif -# Pico - sound -OBJECTS += _build\sound.o _build\sn76496.o _build\ym2612.o -# misc -OBJECTS += _build\unzip.o _build\gzio_symb.o -# CPU cores -ifeq "$(use_musashi)" "1" -GCCDEFINES += -DEMU_M68K -OBJECTS += _build\m68kcpu.o _build\m68kopac.o _build\m68kopdm.o _build\m68kopnz.o _build\m68kops.o -else -GCCDEFINES += -DEMU_C68K -OBJECTS += _build\Cyclone.o +ifeq "$(asm_ym2612)" "1" +CFLAGS += -D_ASM_YM2612_C +OBJ += Pico/sound/ym2612_asm.o endif -ifeq "$(asm_blit)" "1" -OBJECTS += _build\blit_asm.o -else -OBJECTS += _build\blit.o +ifeq "$(asm_misc)" "1" +CFLAGS += -D_ASM_MISC_C +OBJ += Pico/misc_asm.o +OBJ += Pico/cd/misc_asm.o endif -GCCDEFINES += -D_USE_DRZ80 -OBJECTS += _build\DrZ80.o -GCCDEFINES += -D_ASM_YM2612_C -OBJECTS += _build\ym2612_asm.o - - - -# dprint -ifeq "$(dprint)" "1" -GCCDEFINES += -D__DEBUG_PRINT +ifeq "$(asm_cdpico)" "1" +CFLAGS += -D_ASM_CD_PICO_C +OBJ += Pico/cd/pico_asm.o +endif +ifeq "$(asm_cdmemory)" "1" +CFLAGS += -D_ASM_CD_MEMORY_C +OBJ += Pico/cd/memory_asm.o endif - -define crule - @echo * $< - @$(CC) -c $(GCCCOMMFLAGS) $(GCCDEFINES) $(GCCCFLAGS) $< -o $@ -endef - -define cpprule - @echo * $< - @$(CC) -c $(GCCCOMMFLAGS) $(GCCDEFINES) $(GCCCPPFLAGS) $< -o $@ -endef - -define asmrule - @echo * $< - @$(ASM) -marmv4t -mthumb-interwork -o $@ $^ -endef - -# object making rules -_build\App.o : App.cpp - $(cpprule) -_build\Engine.o : Engine.cpp - $(cpprule) -_build\Dialogs.o : Dialogs.cpp - $(cpprule) -_build\CSimpleTextParser.o : CSimpleTextParser.cpp - $(cpprule) - -_build\main.o : engine\main.cpp - $(cpprule) -_build\vid.o : engine\vid.cpp - $(cpprule) -_build\polledas.o: engine\polledas.cpp - $(cpprule) -_build\audio_mediaserver.o : engine\audio_mediaserver.cpp - $(cpprule) -_build\debug.o : engine\debug.cpp - $(cpprule) -_build\blit.o : engine\blit.c - $(crule) - -_build\Area.o : ..\..\Pico\Area.c - $(crule) -_build\Cart.o : ..\..\Pico\Cart.c - $(crule) -_build\Draw.o : ..\..\Pico\Draw.c - $(crule) -_build\Draw2.o : ..\..\Pico\Draw2.c - $(crule) -_build\Memory.o : ..\..\Pico\Memory.c - $(crule) -_build\Misc.o : ..\..\Pico\Misc.c - $(crule) -_build\Pico.o : ..\..\Pico\Pico.c - $(crule) -_build\Sek.o : ..\..\Pico\Sek.c - $(crule) -_build\Utils.o : ..\..\Pico\Utils.c - $(crule) -_build\VideoPort.o : ..\..\Pico\VideoPort.c - $(crule) -_build\sound.o : ..\..\Pico\sound\sound.c - $(crule) -_build\sn76496.o : ..\..\Pico\sound\sn76496.c - $(crule) -_build\ym2612.o : ..\..\Pico\sound\ym2612.c - $(crule) - -_build\unzip.o : ..\..\unzip\unzip.c - $(crule) -_build\gzio_symb.o : ..\..\zlib\gzio_symb.c - $(crule) - -_build\m68kcpu.o : ..\..\musashi\m68kcpu.c - $(crule) -_build\m68kopac.o : ..\..\musashi\m68kopac.c - $(crule) -_build\m68kopdm.o : ..\..\musashi\m68kopdm.c - $(crule) -_build\m68kopnz.o : ..\..\musashi\m68kopnz.c - $(crule) -_build\m68kops.o : ..\..\musashi\m68kops.c - $(crule) - -_build\Cyclone.o : ..\..\cpu\Cyclone\proj\Cyclone.s - $(asmrule) -_build\DrZ80.o : ..\..\cpu\DrZ80\drz80.s - $(asmrule) -_build\draw_asm.o : ..\..\Pico\draw.s - $(asmrule) -_build\draw2_asm.o : ..\..\Pico\draw2.s - $(asmrule) -_build\memory_asm.o : ..\..\Pico\memory.s - $(asmrule) -_build\ym2612_asm.o : ..\..\Pico\sound\ym2612.s - $(asmrule) -_build\blit_asm.o : engine\blit.s - $(asmrule) - - -PicoDrive.exe : $(OBJECTS) - @echo * ld - @$(LD) $(GCCLDFLAGS) -soname PicoDrive{000a0000}[a00010f3].exe --entry _E32Startup -u _E32Startup \ - $(EPOCROOT2)EPOC32\RELEASE\ARMV5\UREL\EEXE.LIB -o "_build\PicoDrive_elf.exe" -Map "_build\PicoDrive.exe.map" $(OBJECTS) $(LIBS) -lsupc++ -lgcc -# @echo * strip -# @$(STRIP) _build\PicoDrive_elf.exe - @echo * elf2e32 - @elf2e32 --sid=0xa00010f3 --heap=0x00000100,0x00800000 --stack=0x00003000 \ - --uid1=0x1000007a --uid2=0x100039ce --uid3=0xa00010f3 \ - --capability=none --fpu=softvfp --targettype=EXE --output="$@" \ - --elfinput="_build\PicoDrive_elf.exe" --linkas=PicoDrive{000a0000}[a00010f3].exe --libpath="$(EPOCLIB)\LIB" -ifeq "$(sis)" "1" - @make -C _out -ifeq "$(up)" "1" - @qup.cmd +# Pico - sound +OBJ += Pico/sound/sound.o +OBJ += Pico/sound/mix_asm.o +OBJ += Pico/sound/sn76496.o Pico/sound/ym2612.o +# zlib +OBJ += zlib/gzio.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o zlib/trees.o \ + zlib/deflate.o zlib/crc32.o zlib/adler32.o zlib/zutil.o zlib/compress.o +# unzip +OBJ += unzip/unzip.o unzip/unzip_stream.o +# CPU cores +ifeq "$(use_musashi)" "1" +CFLAGS += -DEMU_M68K +OBJ += cpu/musashi/m68kops.o cpu/musashi/m68kcpu.o endif +ifeq "$(use_cyclone)" "1" +CFLAGS += -DEMU_C68K +OBJ += cpu/Cyclone/proj/Cyclone.o cpu/Cyclone/tools/idle.o endif - - -MAKEDIRS : _build - -_build : -# @echo * making build dir - @perl -S emkdir.pl $@ - - -# BitMap PicoDrive.mbm - -RESOURCES : rsc\PicoDrive.mbm rsc\PicoDrive.RSC rsc\PicoDrive_reg.RSC rsc\PicoDrive_loc.RSC rsc\PicoDrive.mbg - -rsc\PicoDrive.mbg : rsc\PicoDrive.mbm - -rsc\PicoDrive.mbm : rsc\pico18x18.bmp rsc\pico18x18m.bmp rsc\pico40x40.bmp rsc\pico40x40m.bmp rsc\pico64x64.bmp rsc\pico64x64m.bmp - @echo * $@ - @perl -S epocmbm.pl -h"rsc\PicoDrive.mbg" -o"rsc\PicoDrive.mbm" -l"\Z\Resource\Apps\:rsc" \ - -b"/c24rsc\pico18x18.bmp /8rsc\pico18x18m.bmp /c24rsc\pico40x40.bmp /8rsc\pico40x40m.bmp /c24rsc\pico64x64.bmp /8rsc\pico64x64m.bmp" -l"\Z\Resource\Apps\:rsc" - @perl -S ecopyfile.pl "rsc\PicoDrive.mbg" "$(EPOCROOT2)EPOC32\INCLUDE\PicoDrive.mbg" - -# Resource Z\Resource\Apps\PicoDrive.RSC - -rsc\PicoDrive.RSC : rsc\PicoDrive.rss picodrive.hrh - @echo * $@ - @perl -S epocrc.pl -m045,046,047 -I "." -I- -I "$(EPOCROOT2_NODRV)EPOC32\include" -DLANGUAGE_SC -u "rsc\PicoDrive.rss" -o$@ \ - -h"rsc\PicoDrive.rsg" -t"rsc" -l"Z\Resource\Apps:rsc" - @perl -S ecopyfile.pl "rsc\PicoDrive.rsg" "$(EPOCROOT2)EPOC32\INCLUDE\PicoDrive.RSG" - -# Resource Z\private\10003a3f\apps\PicoDrive_reg.RSC - -rsc\PicoDrive_reg.RSC : rsc\PicoDrive_reg.rss - @echo * $@ - @perl -S epocrc.pl -m045,046,047 -I "." -I- -I "$(EPOCROOT2)EPOC32\include" -DLANGUAGE_SC -u "rsc\PicoDrive_reg.rss" -o$@ \ - -t"rsc" -l"Z\private\10003a3f\apps:rsc" - -# Resource Z\Resource\Apps\PicoDrive_loc.RSC - -rsc\PicoDrive_loc.RSC : rsc\PicoDrive_loc.rss - @echo * $@ - @perl -S epocrc.pl -m045,046,047 -I "." -I- -I "$(EPOCROOT2)EPOC32\include" -DLANGUAGE_SC -u "rsc\PicoDrive_loc.rss" -o$@ \ - -t"rsc" -l"Z\Resource\Apps:rsc" +# drz80 +CFLAGS += -D_USE_DRZ80 +OBJ += cpu/DrZ80/drz80.o +# helix +OBJ += ../common/helix/$(CROSS)helix-mp3.a + + +vpath %.c = ../.. +vpath %.s = ../.. +vpath %.S = ../.. +DIRS = platform platform/common Pico Pico/cd Pico/Pico Pico/sound Pico/carthw/svp \ + zlib unzip cpu cpu/musashi cpu/Cyclone/proj cpu/Cyclone/tools cpu/mz80 cpu/DrZ80 + +ICONS := $(shell echo rsc/*.bmp) +APPICON = $(NAME).mbm +RSCDIR = rsc +REGDIR = rsc +CFLAGS += -I./ -I../../ +CFLAGS += -DUIQ3 -DARM -DNO_SYNC +CFLAGS += -D__DEBUG_PRINT +CFLAGS += -mcpu=arm926ej-s -mtune=arm926ej-s -O3 -ftracer \ + -fstrength-reduce -fomit-frame-pointer -fstrict-aliasing -ffast-math +SFLAGS = -march=armv5t -msoft-float -nostdinc +ASFLAGS = -mcpu=arm926ej-s -mfloat-abi=soft +export CFLAGS +SRCRES := $(shell echo rsc/*.rss) +EPOCRCFLAGS += -I./ + +all: mkdirs sis + +include uiq3.mak +include ../common/common_arm.mak + + +$(NAME).mbg $(NAME).mbm : $(ICONS) + @echo "Creating multibitmap file..." + $(BMCONV) /h$(NAME).mbg $(NAME).mbm \ + /c24rsc/pico18x18.bmp /8rsc/pico18x18m.bmp \ + /c24rsc/pico40x40.bmp /8rsc/pico40x40m.bmp \ + /c24rsc/pico64x64.bmp /8rsc/pico64x64m.bmp + + +engine/blit_asm.o : engine/blit.s + @echo ">>>" $@ + $(AS) $(ASFLAGS) $< -o $@ + +# App.o can't be optimized +#App.o : App.cpp +# $(CC) $(CXXFLAGS) -O0 -c $< -o $@ diff --git a/platform/uiq3/Makefile.old b/platform/uiq3/Makefile.old new file mode 100644 index 0000000..fa14569 --- /dev/null +++ b/platform/uiq3/Makefile.old @@ -0,0 +1,307 @@ +# makefile for GCCE + +# settings +#dprint = 1 +asm_memory = 1 +asm_render = 1 +asm_blit = 1 +#use_musashi = 1 +#up = 1 +#sis = 1 + +# targets +all: $(EPOCROOT2)epoc32 MAKEDIRS RESOURCES PicoDrive.exe + +clean : + @perl -S ermdir.pl _build + @erase 2>>nul rsc\*.rsc + @erase 2>>nul rsc\*.rsg + @erase 2>>nul rsc\PicoDrive.mb? + + +# paths +$(EPOCROOT2)epoc32 : + @echo Please set EPOCROOT2 environmental variable to full path to your SDK + @echo with ending slash (something like C:\Uiq_21\) + @cd : 2> NUL # do something stupid to make it silently fail + +# resource compiler hates drive lettered paths +EPOCROOT2_NODRV = $(filter \\%,$(subst :, ,$(EPOCROOT2))) +EPOCLIB = $(EPOCROOT2)EPOC32\RELEASE\ARMV5 + +# C/C++ Compiler +CC=arm-none-symbianelf-gcc + +# Linker +LD=arm-none-symbianelf-ld + +# Assembler +ASM=arm-none-symbianelf-as + +# Archiver +AR=arm-none-symbianelf-ar + +# Strip +STRIP=arm-none-symbianelf-strip + +# gcc config +GCCDEFINES = -DNDEBUG -D_UNICODE -D__GCCE__ -D__SYMBIAN32__ -D__EPOC32__ -D__MARM__ \ + -D__EABI__ -D__MARM_ARMV5__ -D__EXE__ -D__SUPPORT_CPP_EXCEPTIONS__ \ + -D__MARM_ARMV5__ -D__PRODUCT_INCLUDE__=\"$(EPOCROOT2)epoc32/include/variant/UIQ_3.0.hrh\" + +GCCDEFINES += -D_UNZIP_SUPPORT -D__BROKEN_FWRITE + +# 'CSL Arm Toolchain' stuff must be specified after Symbian includes +GCCINCLUDES = -I "$(EPOCROOT2)epoc32\include\variant" -I "$(EPOCROOT2)EPOC32\INCLUDE" -I "$(EPOCROOT2)EPOC32\INCLUDE\LIBC" \ + -I "$(EPOCROOT2)\CSL Arm Toolchain\lib\gcc\arm-none-symbianelf\3.4.3\include" -I. + +# -funit-at-a-time is not compatible with SDK, it either has linker problems or does not start on device +GCCCOMMFLAGS = -Wall -Wno-unknown-pragmas -fexceptions -march=armv5t -mapcs -pipe -nostdinc -msoft-float \ + $(GCCINCLUDES) -include "$(EPOCROOT2)EPOC32/INCLUDE/GCCE/GCCE.h" -marm + +GCCCPPFLAGS = -x c++ -Wno-ctor-dtor-privacy -O3 -fno-unit-at-a-time +GCCCFLAGS = -x c -O3 -fno-unit-at-a-time + +GCCLDFLAGS = -L"$(EPOCROOT2)CSL Arm Toolchain\arm-none-symbianelf\lib" \ + -L"$(EPOCROOT2)CSL Arm Toolchain\lib\gcc\arm-none-symbianelf\3.4.3" \ + --target1-abs --no-undefined -nostdlib -shared -Ttext 0x8000 -Tdata 0x400000 --default-symver + +# libs +LIBS = \ + $(EPOCLIB)\LIB\ESTLIB.dso \ + $(EPOCLIB)\urel\qikalloc.lib \ + $(EPOCLIB)\LIB\euser.dso \ + $(EPOCLIB)\LIB\apparc.dso \ + $(EPOCLIB)\LIB\cone.dso \ + $(EPOCLIB)\LIB\eikcore.dso \ + $(EPOCLIB)\LIB\eikcoctl.dso \ + $(EPOCLIB)\LIB\qikcore.dso \ + $(EPOCLIB)\LIB\qikdlg.dso \ + $(EPOCLIB)\LIB\etext.dso \ + $(EPOCLIB)\LIB\bafl.dso \ + $(EPOCLIB)\LIB\efsrv.dso \ + $(EPOCLIB)\LIB\eikctl.dso \ + $(EPOCLIB)\LIB\WS32.dso \ + $(EPOCLIB)\LIB\EIKDLG.dso \ + $(EPOCLIB)\LIB\GDI.dso \ + $(EPOCLIB)\LIB\estor.dso \ + $(EPOCLIB)\LIB\EZLIB.dso \ + $(EPOCLIB)\LIB\HAL.dso \ + $(EPOCLIB)\LIB\mediaclient.dso \ + $(EPOCLIB)\LIB\mediaclientaudiostream.dso + +LIBS += \ + $(EPOCLIB)\LIB\qikallocdll.dso \ + $(EPOCLIB)\UREL\usrt2_2.lib \ + $(EPOCLIB)\LIB\dfpaeabi.dso \ + $(EPOCLIB)\LIB\dfprvct2_2.dso \ + $(EPOCLIB)\LIB\drtaeabi.dso \ + $(EPOCLIB)\LIB\scppnwdl.dso \ + $(EPOCLIB)\LIB\drtrvct2_2.dso + + +# objects + +# launcher +OBJECTS += _build\App.o _build\Engine.o _build\Dialogs.o _build\CSimpleTextParser.o +# engine +OBJECTS += _build\main.o _build\vid.o _build\polledas.o _build\audio_mediaserver.o _build\debug.o + +# Pico +OBJECTS += _build\Area.o _build\Cart.o _build\Utils.o _build\Memory.o _build\Misc.o \ + _build\Pico.o _build\Sek.o _build\VideoPort.o _build\Draw2.o _build\Draw.o +# asm stuff +ifeq "$(asm_render)" "1" +GCCDEFINES += -D_ASM_DRAW_C +OBJECTS += _build\draw_asm.o _build\draw2_asm.o +endif +ifeq "$(asm_memory)" "1" +GCCDEFINES += -D_ASM_MEMORY_C +OBJECTS += _build\memory_asm.o +endif +# Pico - sound +OBJECTS += _build\sound.o _build\sn76496.o _build\ym2612.o +# misc +OBJECTS += _build\unzip.o _build\gzio_symb.o +# CPU cores +ifeq "$(use_musashi)" "1" +GCCDEFINES += -DEMU_M68K +OBJECTS += _build\m68kcpu.o _build\m68kopac.o _build\m68kopdm.o _build\m68kopnz.o _build\m68kops.o +else +GCCDEFINES += -DEMU_C68K +OBJECTS += _build\Cyclone.o +endif +ifeq "$(asm_blit)" "1" +OBJECTS += _build\blit_asm.o +else +OBJECTS += _build\blit.o +endif +GCCDEFINES += -D_USE_DRZ80 +OBJECTS += _build\DrZ80.o +GCCDEFINES += -D_ASM_YM2612_C +OBJECTS += _build\ym2612_asm.o + + + +# dprint +ifeq "$(dprint)" "1" +GCCDEFINES += -D__DEBUG_PRINT +endif + + +define crule + @echo * $< + @$(CC) -c $(GCCCOMMFLAGS) $(GCCDEFINES) $(GCCCFLAGS) $< -o $@ +endef + +define cpprule + @echo * $< + @$(CC) -c $(GCCCOMMFLAGS) $(GCCDEFINES) $(GCCCPPFLAGS) $< -o $@ +endef + +define asmrule + @echo * $< + @$(ASM) -marmv4t -mthumb-interwork -o $@ $^ +endef + +# object making rules +_build\App.o : App.cpp + $(cpprule) +_build\Engine.o : Engine.cpp + $(cpprule) +_build\Dialogs.o : Dialogs.cpp + $(cpprule) +_build\CSimpleTextParser.o : CSimpleTextParser.cpp + $(cpprule) + +_build\main.o : engine\main.cpp + $(cpprule) +_build\vid.o : engine\vid.cpp + $(cpprule) +_build\polledas.o: engine\polledas.cpp + $(cpprule) +_build\audio_mediaserver.o : engine\audio_mediaserver.cpp + $(cpprule) +_build\debug.o : engine\debug.cpp + $(cpprule) +_build\blit.o : engine\blit.c + $(crule) + +_build\Area.o : ..\..\Pico\Area.c + $(crule) +_build\Cart.o : ..\..\Pico\Cart.c + $(crule) +_build\Draw.o : ..\..\Pico\Draw.c + $(crule) +_build\Draw2.o : ..\..\Pico\Draw2.c + $(crule) +_build\Memory.o : ..\..\Pico\Memory.c + $(crule) +_build\Misc.o : ..\..\Pico\Misc.c + $(crule) +_build\Pico.o : ..\..\Pico\Pico.c + $(crule) +_build\Sek.o : ..\..\Pico\Sek.c + $(crule) +_build\Utils.o : ..\..\Pico\Utils.c + $(crule) +_build\VideoPort.o : ..\..\Pico\VideoPort.c + $(crule) +_build\sound.o : ..\..\Pico\sound\sound.c + $(crule) +_build\sn76496.o : ..\..\Pico\sound\sn76496.c + $(crule) +_build\ym2612.o : ..\..\Pico\sound\ym2612.c + $(crule) + +_build\unzip.o : ..\..\unzip\unzip.c + $(crule) +_build\gzio_symb.o : ..\..\zlib\gzio_symb.c + $(crule) + +_build\m68kcpu.o : ..\..\musashi\m68kcpu.c + $(crule) +_build\m68kopac.o : ..\..\musashi\m68kopac.c + $(crule) +_build\m68kopdm.o : ..\..\musashi\m68kopdm.c + $(crule) +_build\m68kopnz.o : ..\..\musashi\m68kopnz.c + $(crule) +_build\m68kops.o : ..\..\musashi\m68kops.c + $(crule) + +_build\Cyclone.o : ..\..\cpu\Cyclone\proj\Cyclone.s + $(asmrule) +_build\DrZ80.o : ..\..\cpu\DrZ80\drz80.s + $(asmrule) +_build\draw_asm.o : ..\..\Pico\draw.s + $(asmrule) +_build\draw2_asm.o : ..\..\Pico\draw2.s + $(asmrule) +_build\memory_asm.o : ..\..\Pico\memory.s + $(asmrule) +_build\ym2612_asm.o : ..\..\Pico\sound\ym2612.s + $(asmrule) +_build\blit_asm.o : engine\blit.s + $(asmrule) + + +PicoDrive.exe : $(OBJECTS) + @echo * ld + @$(LD) $(GCCLDFLAGS) -soname PicoDrive{000a0000}[a00010f3].exe --entry _E32Startup -u _E32Startup \ + $(EPOCROOT2)EPOC32\RELEASE\ARMV5\UREL\EEXE.LIB -o "_build\PicoDrive_elf.exe" -Map "_build\PicoDrive.exe.map" $(OBJECTS) $(LIBS) -lsupc++ -lgcc +# @echo * strip +# @$(STRIP) _build\PicoDrive_elf.exe + @echo * elf2e32 + @elf2e32 --sid=0xa00010f3 --heap=0x00000100,0x00800000 --stack=0x00003000 \ + --uid1=0x1000007a --uid2=0x100039ce --uid3=0xa00010f3 \ + --capability=none --fpu=softvfp --targettype=EXE --output="$@" \ + --elfinput="_build\PicoDrive_elf.exe" --linkas=PicoDrive{000a0000}[a00010f3].exe --libpath="$(EPOCLIB)\LIB" +ifeq "$(sis)" "1" + @make -C _out +ifeq "$(up)" "1" + @qup.cmd +endif +endif + + +MAKEDIRS : _build + +_build : +# @echo * making build dir + @perl -S emkdir.pl $@ + + +# BitMap PicoDrive.mbm + +RESOURCES : rsc\PicoDrive.mbm rsc\PicoDrive.RSC rsc\PicoDrive_reg.RSC rsc\PicoDrive_loc.RSC rsc\PicoDrive.mbg + +rsc\PicoDrive.mbg : rsc\PicoDrive.mbm + +rsc\PicoDrive.mbm : rsc\pico18x18.bmp rsc\pico18x18m.bmp rsc\pico40x40.bmp rsc\pico40x40m.bmp rsc\pico64x64.bmp rsc\pico64x64m.bmp + @echo * $@ + @perl -S epocmbm.pl -h"rsc\PicoDrive.mbg" -o"rsc\PicoDrive.mbm" -l"\Z\Resource\Apps\:rsc" \ + -b"/c24rsc\pico18x18.bmp /8rsc\pico18x18m.bmp /c24rsc\pico40x40.bmp /8rsc\pico40x40m.bmp /c24rsc\pico64x64.bmp /8rsc\pico64x64m.bmp" -l"\Z\Resource\Apps\:rsc" + @perl -S ecopyfile.pl "rsc\PicoDrive.mbg" "$(EPOCROOT2)EPOC32\INCLUDE\PicoDrive.mbg" + +# Resource Z\Resource\Apps\PicoDrive.RSC + +rsc\PicoDrive.RSC : rsc\PicoDrive.rss picodrive.hrh + @echo * $@ + @perl -S epocrc.pl -m045,046,047 -I "." -I- -I "$(EPOCROOT2_NODRV)EPOC32\include" -DLANGUAGE_SC -u "rsc\PicoDrive.rss" -o$@ \ + -h"rsc\PicoDrive.rsg" -t"rsc" -l"Z\Resource\Apps:rsc" + @perl -S ecopyfile.pl "rsc\PicoDrive.rsg" "$(EPOCROOT2)EPOC32\INCLUDE\PicoDrive.RSG" + +# Resource Z\private\10003a3f\apps\PicoDrive_reg.RSC + +rsc\PicoDrive_reg.RSC : rsc\PicoDrive_reg.rss + @echo * $@ + @perl -S epocrc.pl -m045,046,047 -I "." -I- -I "$(EPOCROOT2)EPOC32\include" -DLANGUAGE_SC -u "rsc\PicoDrive_reg.rss" -o$@ \ + -t"rsc" -l"Z\private\10003a3f\apps:rsc" + +# Resource Z\Resource\Apps\PicoDrive_loc.RSC + +rsc\PicoDrive_loc.RSC : rsc\PicoDrive_loc.rss + @echo * $@ + @perl -S epocrc.pl -m045,046,047 -I "." -I- -I "$(EPOCROOT2)EPOC32\include" -DLANGUAGE_SC -u "rsc\PicoDrive_loc.rss" -o$@ \ + -t"rsc" -l"Z\Resource\Apps:rsc" diff --git a/platform/uiq3/_out/Makefile b/platform/uiq3/_out/Makefile deleted file mode 100644 index 1e17672..0000000 --- a/platform/uiq3/_out/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: PicoDrive.SIS - -PicoDrive.SIS: ..\PicoDrive.exe ..\rsc\PicoDrive.rsc ..\rsc\PicoDrive.mbm ..\rsc\PicoDrive_loc.rsc ..\rsc\PicoDrive_reg.rsc - makesis PicoDrive.pkg - -clean: - @erase 2>>nul PicoDrive.pkg diff --git a/platform/uiq3/_out/PicoDrive.pkg b/platform/uiq3/_out/PicoDrive.pkg deleted file mode 100644 index 63412a5..0000000 --- a/platform/uiq3/_out/PicoDrive.pkg +++ /dev/null @@ -1,25 +0,0 @@ -; - -; List of localised vendor names - one per language. At least one must be provided (English [EN]). -; List must correspond to list of languages specified elsewhere in the .pkg -%{"notaz"} - -; The non-localised, globally unique vendor name (mandatory) -:"notaz" - -; Package header -; Name, UID3, Major, Minor, Build, Package-type -#{"PicoDrive"}, (0xA00010F3), 0, 96, 0, TYPE=SA - -; ProductID for UIQ 3.0 -; Product/platform version UID, Major, Minor, Build, Component name -(0x101F6300), 3, 0, 0, {"UIQ30ProductID"} - - -; Files to install for the application -; If you move the example to another destination then you also need to change these paths. -"..\PicoDrive.exe"-"!:\sys\bin\PicoDrive.exe" -"..\rsc\PicoDrive.rsc"-"!:\resource\apps\PicoDrive.rsc" -"..\rsc\PicoDrive.mbm"-"!:\resource\apps\PicoDrive.mbm" -"..\rsc\PicoDrive_loc.rsc"-"!:\resource\apps\PicoDrive_loc.rsc" -"..\rsc\PicoDrive_reg.rsc"-"!:\private\10003a3f\import\apps\PicoDrive_reg.rsc" diff --git a/platform/uiq3/_out/PicoDrive.zip b/platform/uiq3/_out/PicoDrive.zip deleted file mode 100644 index ccb557d664b79d29523f0aced3bd139a875c12a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 157336 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

vtayF&R`W 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�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≻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�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%0Aqq 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 diff --git a/platform/uiq3/_out/PicoDrive_gcce.pkg b/platform/uiq3/_out/PicoDrive_gcce.pkg deleted file mode 100644 index 44c88f3..0000000 --- a/platform/uiq3/_out/PicoDrive_gcce.pkg +++ /dev/null @@ -1,25 +0,0 @@ -; - -; List of localised vendor names - one per language. At least one must be provided (English [EN]). -; List must correspond to list of languages specified elsewhere in the .pkg -%{"notaz"} - -; The non-localised, globally unique vendor name (mandatory) -:"notaz" - -; Package header -; Name, UID3, Major, Minor, Build, Package-type -#{"PicoDrive"}, (0xA00010F3), 0, 92, 0, TYPE=SA - -; ProductID for UIQ 3.0 -; Product/platform version UID, Major, Minor, Build, Component name -(0x101F6300), 3, 0, 0, {"UIQ30ProductID"} - - -; Files to install for the application -; If you move the example to another destination then you also need to change these paths. -"..\..\..\..\..\..\UIQ3SDK\epoc32\release\gcce\urel\PicoDrive.exe"-"!:\sys\bin\PicoDrive.exe" -"..\..\..\..\..\..\UIQ3SDK\epoc32\data\Z\Resource\Apps\PicoDrive.rsc"-"!:\resource\apps\PicoDrive.rsc" -"..\..\..\..\..\..\UIQ3SDK\epoc32\data\Z\Resource\Apps\PicoDrive_loc.rsc"-"!:\resource\apps\PicoDrive_loc.rsc" -"..\..\..\..\..\..\UIQ3SDK\epoc32\data\z\Private\10003a3f\Apps\PicoDrive_reg.rsc"-"!:\private\10003a3f\import\apps\PicoDrive_reg.rsc" -"..\..\..\..\..\..\UIQ3SDK\epoc32\data\Z\Resource\Apps\PicoDrive.mbm"-"!:\resource\apps\PicoDrive.mbm" diff --git a/platform/uiq3/_out/config.txt b/platform/uiq3/config.txt similarity index 100% rename from platform/uiq3/_out/config.txt rename to platform/uiq3/config.txt diff --git a/platform/uiq3/emu.c b/platform/uiq3/emu.c new file mode 100644 index 0000000..71bf872 --- /dev/null +++ b/platform/uiq3/emu.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include "../common/emu.h" +#include "../common/config.h" +#include "../common/menu.h" +#include "Pico/PicoInt.h" + +const char * const keyNames[] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +}; + +int emu_getMainDir(char *dst, int len) +{ + strcpy(dst, "D:\\other\\PicoDrive\\"); + return strlen(dst); +} + +void emu_Init(void) +{ + int ret; + + // make dirs for saves, cfgs, etc. + ret = mkdir("D:\\other\\PicoDrive", 0777); + if (ret == 0) + { + mkdir("D:\\other\\PicoDrive\\mds", 0777); + mkdir("D:\\other\\PicoDrive\\srm", 0777); + mkdir("D:\\other\\PicoDrive\\brm", 0777); + } + + emu_prepareDefaultConfig(); + config_readlrom("D:\\other\\PicoDrive\\config.cfg"); + emu_ReadConfig(0, 0); + //PicoInit(); +} + +void emu_Deinit(void) +{ + // saves volume and last ROM + emu_WriteConfig(0); + //PicoExit(); +} + +void menu_romload_prepare(const char *rom_name) +{ +} + +void menu_romload_end(void) +{ +} + +void emu_prepareDefaultConfig(void) +{ + memset(&defaultConfig, 0, sizeof(defaultConfig)); + defaultConfig.EmuOpt = 0x1d | 0x680; // | confirm_save, cd_leds, 16bit rend + defaultConfig.s_PicoOpt = 0x0f | POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_SVP_DRC|POPT_ACC_SPRITES; + defaultConfig.s_PsndRate = 22050; + defaultConfig.s_PicoRegion = 0; // auto + defaultConfig.s_PicoAutoRgnOrder = 0x184; // US, EU, JP + defaultConfig.s_PicoCDBuffers = 0; + defaultConfig.Frameskip = -1; // auto + defaultConfig.volume = 80; + defaultConfig.scaling = 0; +} + +/* used by config engine only, not actual menus */ +menu_entry opt_entries[] = +{ + { NULL, MB_NONE, MA_OPT_RENDERER, NULL, 0, 0, 0, 1, 1 }, + { "Scaling", MB_RANGE, MA_OPT_SCALING, ¤tConfig.scaling, 0, 0, 2, 1, 1 }, + { "Rotation", MB_RANGE, MA_OPT_ROTATION, ¤tConfig.rotation, 0, 0, 3, 1, 1 }, + { "Accurate sprites", MB_ONOFF, MA_OPT_ACC_SPRITES, &PicoOpt, 0x080, 0, 0, 0, 1 }, + { "Show FPS", MB_ONOFF, MA_OPT_SHOW_FPS, ¤tConfig.EmuOpt, 0x002, 0, 0, 1, 1 }, + { NULL, MB_RANGE, MA_OPT_FRAMESKIP, ¤tConfig.Frameskip, 0, -1, 16, 1, 1 }, + { "Enable sound", MB_ONOFF, MA_OPT_ENABLE_SOUND, ¤tConfig.EmuOpt, 0x004, 0, 0, 1, 1 }, + { NULL, MB_NONE, MA_OPT_SOUND_QUALITY, NULL, 0, 0, 0, 1, 1 }, + { NULL, MB_NONE, MA_OPT_REGION, NULL, 0, 0, 0, 1, 1 }, + { "Use SRAM/BRAM savestates", MB_ONOFF, MA_OPT_SRAM_STATES, ¤tConfig.EmuOpt, 0x001, 0, 0, 1, 1 }, +}; + +#define OPT_ENTRY_COUNT (sizeof(opt_entries) / sizeof(opt_entries[0])) +const int opt_entry_count = OPT_ENTRY_COUNT; + +menu_entry opt2_entries[] = +{ + { "Disable sprite limit", MB_ONOFF, MA_OPT2_NO_SPRITE_LIM, &PicoOpt, 0x40000, 0, 0, 1, 1 }, + { "Emulate Z80", MB_ONOFF, MA_OPT2_ENABLE_Z80, &PicoOpt, 0x00004, 0, 0, 1, 1 }, + { "Emulate YM2612 (FM)", MB_ONOFF, MA_OPT2_ENABLE_YM2612, &PicoOpt, 0x00001, 0, 0, 1, 1 }, + { "Emulate SN76496 (PSG)", MB_ONOFF, MA_OPT2_ENABLE_SN76496,&PicoOpt, 0x00002, 0, 0, 1, 1 }, + { "gzip savestates", MB_ONOFF, MA_OPT2_GZIP_STATES, ¤tConfig.EmuOpt, 0x0008, 0, 0, 1, 1 }, + { "SVP dynarec", MB_ONOFF, MA_OPT2_SVP_DYNAREC, &PicoOpt, 0x20000, 0, 0, 1, 1 }, + { "Disable idle loop patching",MB_ONOFF, MA_OPT2_NO_IDLE_LOOPS, &PicoOpt, 0x80000, 0, 0, 1, 1 }, +}; + +#define OPT2_ENTRY_COUNT (sizeof(opt2_entries) / sizeof(opt2_entries[0])) +const int opt2_entry_count = OPT2_ENTRY_COUNT; + +menu_entry cdopt_entries[] = +{ + { "CD LEDs", MB_ONOFF, MA_CDOPT_LEDS, ¤tConfig.EmuOpt, 0x0400, 0, 0, 1, 1 }, + { "CDDA audio (using mp3s)", MB_ONOFF, MA_CDOPT_CDDA, &PicoOpt, 0x0800, 0, 0, 1, 1 }, + { "PCM audio", MB_ONOFF, MA_CDOPT_PCM, &PicoOpt, 0x0400, 0, 0, 1, 1 }, + { NULL, MB_NONE, MA_CDOPT_READAHEAD, NULL, 0, 0, 0, 1, 1 }, + { "SaveRAM cart", MB_ONOFF, MA_CDOPT_SAVERAM, &PicoOpt, 0x8000, 0, 0, 1, 1 }, + { "Scale/Rot. fx (slow)", MB_ONOFF, MA_CDOPT_SCALEROT_CHIP,&PicoOpt, 0x1000, 0, 0, 1, 1 }, + { "Better sync (slow)", MB_ONOFF, MA_CDOPT_BETTER_SYNC, &PicoOpt, 0x2000, 0, 0, 1, 1 }, +}; + +#define CDOPT_ENTRY_COUNT (sizeof(cdopt_entries) / sizeof(cdopt_entries[0])) +const int cdopt_entry_count = CDOPT_ENTRY_COUNT; + +menu_entry ctrlopt_entries[] = +{ + { "6 button pad", MB_ONOFF, MA_OPT_6BUTTON_PAD, &PicoOpt, 0x020, 0, 0, 1, 1 }, + { "Turbo rate", MB_RANGE, MA_CTRL_TURBO_RATE, ¤tConfig.turbo_rate, 0, 1, 30, 1, 1 }, +}; + +#define CTRLOPT_ENTRY_COUNT (sizeof(ctrlopt_entries) / sizeof(ctrlopt_entries[0])) +const int ctrlopt_entry_count = CTRLOPT_ENTRY_COUNT; + +me_bind_action emuctrl_actions[] = +{ + { "Load State ", 1<<28 }, + { "Save State ", 1<<27 }, + { "Pause Emu ", 1<<26 }, + { "Switch Renderer", 1<<25 }, + { "Prev save slot ", 1<<23 }, + { "Next save slot ", 1<<22 }, + { "Volume down ", 1<<21 }, + { "Volume up ", 1<<20 }, + { NULL, 0 } +}; + + diff --git a/platform/uiq3/emu.h b/platform/uiq3/emu.h new file mode 100644 index 0000000..c5710f2 --- /dev/null +++ b/platform/uiq3/emu.h @@ -0,0 +1,11 @@ +#ifdef __cplusplus +extern "C" { +#endif + +void emu_Init(void); +void emu_Deinit(void); + +#ifdef __cplusplus +} // extern "C" +#endif + diff --git a/platform/uiq3/engine/audio_mediaserver.cpp b/platform/uiq3/engine/audio_mediaserver.cpp index aff12d1..c9788d3 100644 --- a/platform/uiq3/engine/audio_mediaserver.cpp +++ b/platform/uiq3/engine/audio_mediaserver.cpp @@ -15,6 +15,7 @@ #include "audio_mediaserver.h" #include "debug.h" +//#define DEBUG_UNDERFLOWS //#undef DEBUGPRINT //#define DEBUGPRINT(x...) @@ -31,16 +32,16 @@ const TInt KMaxUnderflows = 50; // max underflows/API errors we are going allow * *******************************************/ -CGameAudioMS::CGameAudioMS(TInt aRate, TBool aStereo, TInt aWritesPerSec) -: iRate(aRate), iStereo(aStereo), iWritesPerSec(aWritesPerSec) +CGameAudioMS::CGameAudioMS(TInt aRate, TBool aStereo, TInt aWritesPerSec, TInt aVolume) +: iRate(aRate), iStereo(aStereo), iWritesPerSec(aWritesPerSec), iVolume(aVolume) { } -CGameAudioMS* CGameAudioMS::NewL(TInt aRate, TBool aStereo, TInt aWritesPerSec) +CGameAudioMS* CGameAudioMS::NewL(TInt aRate, TBool aStereo, TInt aWritesPerSec, TInt aVolume) { - DEBUGPRINT(_L("CGameAudioMS::NewL(%i, %i, %i)"), aRate, aStereo, aWritesPerSec); - CGameAudioMS* self = new(ELeave) CGameAudioMS(aRate, aStereo, aWritesPerSec); + DEBUGPRINT(_L("CGameAudioMS::NewL(%i, %i, %i, %i)"), aRate, aStereo, aWritesPerSec, aVolume); + CGameAudioMS* self = new(ELeave) CGameAudioMS(aRate, aStereo, aWritesPerSec, aVolume); CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(); // self @@ -85,25 +86,29 @@ void CGameAudioMS::ConstructL() iMdaAudioDataSettings.iCaps = TMdaAudioDataSettings::ESampleRateFixed | iMdaAudioDataSettings.iSampleRate; iMdaAudioDataSettings.iFlags = TMdaAudioDataSettings::ENoNetworkRouting; - int pcmFrames = iRate / iWritesPerSec; - pcmFrames += iRate - (iRate / iWritesPerSec) * iWritesPerSec; // add division remainder too for our buffer size - iBufferedFrames = iWritesPerSec / KUpdatesPerSec; + iMaxWriteSamples = iRate / iWritesPerSec; + if (iRate % iWritesPerSec) + iMaxWriteSamples++; + int bufferedFrames = iWritesPerSec / KUpdatesPerSec; - TInt bytesPerFrame = pcmFrames << (iStereo?2:1); + iBufferSize = iMaxWriteSamples * (iStereo ? 4 : 2); + iBufferSize *= bufferedFrames; for (TInt i=0 ; iDes().FillZ (bytesPerFrame * iBufferedFrames); + iSoundBuffers[i] = HBufC8::NewL(iBufferSize); + iSoundBuffers[i]->Des().FillZ (iBufferSize); } iCurrentBuffer = 0; iCurrentBufferSize = 0; + DEBUGPRINT(_L("sound: iMaxWriteSamples: %i, iBufferSize: %i"), iMaxWriteSamples, iBufferSize); + // here we actually test if we can create and open CMdaAudioOutputStream at all, but really create and use it later. iMdaAudioOutputStream = CMdaAudioOutputStream::NewL(iListener, iServer); - if(iMdaAudioOutputStream) { - iVolume = iMdaAudioOutputStream->MaxVolume(); - DEBUGPRINT(_L("MaxVolume: %i"), iVolume); + if (iMdaAudioOutputStream) { + if (iVolume < 0 || iVolume > iMdaAudioOutputStream->MaxVolume()) + iVolume = iMdaAudioOutputStream->MaxVolume(); delete iMdaAudioOutputStream; iMdaAudioOutputStream = 0; } @@ -113,11 +118,18 @@ void CGameAudioMS::ConstructL() // to be used when iSoundBuffers are used directly TInt16 *CGameAudioMS::NextFrameL(TInt aPcmFrames) { - iCurrentPosition += aPcmFrames << (iStereo?1:0); - iCurrentBufferSize += aPcmFrames << (iStereo?2:1); + TInt mul = iStereo ? 4 : 2; + TInt bytes = aPcmFrames * mul; + iCurrentPosition += bytes / 2; + iCurrentBufferSize += bytes; - if (++iFrameCount == iBufferedFrames) + if (aPcmFrames > iMaxWriteSamples) { + DEBUGPRINT(_L("too many samples: %i > %i"), aPcmFrames, iMaxWriteSamples); + } + + if (iCurrentBufferSize + iMaxWriteSamples * mul > iBufferSize) { + //DEBUGPRINT(_L("write on iCurrentBufferSize %i"), iCurrentBufferSize); WriteBlockL(); } @@ -162,9 +174,9 @@ void CGameAudioMS::WriteBlockL() } } - iFrameCount = 0; if (++iCurrentBuffer == KSoundBuffers) iCurrentBuffer = 0; + iSoundBuffers[iCurrentBuffer]->Des().SetMax(); iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr(); iCurrentBufferSize = 0; } @@ -189,7 +201,6 @@ TInt16 *CGameAudioMS::ResumeL() iListener.iIsOpen = ETrue; iListener.iUnderflowed = 1; iListener.iLastError = 0; - iFrameCount = 0; iCurrentBufferSize = 0; iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr(); return iCurrentPosition; @@ -198,7 +209,9 @@ TInt16 *CGameAudioMS::ResumeL() // handles underflow condition void CGameAudioMS::UnderflowedL() { +#ifdef DEBUG_UNDERFLOWS DEBUGPRINT(_L("UnderflowedL()")); +#endif if (iListener.iLastError != KErrUnderflow) { @@ -243,23 +256,30 @@ void CGameAudioMS::WaitForOpenToCompleteL() User::LeaveIfError(KErrNotSupported); } -void CGameAudioMS::ChangeVolume(TInt aUp) +TInt CGameAudioMS::ChangeVolume(TInt aUp) { //DEBUGPRINT(_L("CGameAudioMS::ChangeVolume(%i)"), aUp); if (iMdaAudioOutputStream) { if (aUp) { - if (iVolume < iMdaAudioOutputStream->MaxVolume()) iVolume+=5; + iVolume += 5; + if (iVolume > iMdaAudioOutputStream->MaxVolume()) + iVolume = iMdaAudioOutputStream->MaxVolume(); } else { - if (iVolume > 0) iVolume-=5; + iVolume -= 5; + if (iVolume < 0) iVolume = 0; } iMdaAudioOutputStream->SetVolume(iVolume); } + + return iVolume; } void TGameAudioEventListener::MaoscOpenComplete(TInt aError) { +#ifdef DEBUG_UNDERFLOWS DEBUGPRINT(_L("CGameAudioMS::MaoscOpenComplete, error=%d"), aError); +#endif iIsOpen = ETrue; if(aError) { @@ -284,7 +304,9 @@ void TGameAudioEventListener::MaoscBufferCopied(TInt aError, const TDesC8& aBuff void TGameAudioEventListener::MaoscPlayComplete(TInt aError) { +#ifdef DEBUG_UNDERFLOWS DEBUGPRINT(_L("CGameAudioMS::MaoscPlayComplete: %i"), aError); +#endif if(aError) { iLastError = aError; iUnderflowed++; // never happened to me while testing, but just in case diff --git a/platform/uiq3/engine/audio_mediaserver.h b/platform/uiq3/engine/audio_mediaserver.h index 25d1027..fc94f7c 100644 --- a/platform/uiq3/engine/audio_mediaserver.h +++ b/platform/uiq3/engine/audio_mediaserver.h @@ -15,11 +15,11 @@ #ifndef __AUDIO_MEDIASERVER_H #define __AUDIO_MEDIASERVER_H -#include -#include +#include +#include //#include "audio.h" -#include "polledas.h" +#include "PolledAS.h" const TInt KSoundBuffers = 4; @@ -44,12 +44,12 @@ public: // implements IGameAudio TInt16 *NextFrameL(TInt aPcmFrames); TInt16 *ResumeL(); void Pause(); - void ChangeVolume(TInt aUp); + TInt ChangeVolume(TInt aUp); public: ~CGameAudioMS(); - CGameAudioMS(TInt aRate, TBool aStereo, TInt aWritesPerSec); - static CGameAudioMS* NewL(TInt aRate, TBool aStereo, TInt aWritesPerSec); + CGameAudioMS(TInt aRate, TBool aStereo, TInt aWritesPerSec, TInt aVolume); + static CGameAudioMS* NewL(TInt aRate, TBool aStereo, TInt aWritesPerSec, TInt aVolume); protected: void WriteBlockL(); @@ -70,12 +70,12 @@ protected: CPolledActiveScheduler *iScheduler; HBufC8* iSoundBuffers[KSoundBuffers]; - TInt iWritesPerSec; - TInt iBufferedFrames; + TInt iWritesPerSec; // fps, may be more actual writes + TInt iMaxWriteSamples; // max samples per write TInt16* iCurrentPosition; - TInt iCurrentBuffer; - TInt iCurrentBufferSize; - TInt iFrameCount; + TInt iCurrentBuffer; // active buffer + TInt iCurrentBufferSize; // bytes filled in buffer + TInt iBufferSize; CMdaServer* iServer; TInt64 iTime; diff --git a/platform/uiq3/engine/blit.s b/platform/uiq3/engine/blit.s index 521c31a..c377a76 100644 --- a/platform/uiq3/engine/blit.s +++ b/platform/uiq3/engine/blit.s @@ -1,3 +1,4 @@ +@ vim:filetype=armasm @ some color conversion and blitting routines @ (c) Copyright 2006, notaz @@ -693,3 +694,13 @@ vidClear: orr r12, #(240/16-1)<<16 b .loopVidClear +@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +.equ EExecSetExceptionHandler, (90) + +.global my_SetExceptionHandler + +my_SetExceptionHandler: + mov ip, lr + swi EExecSetExceptionHandler + diff --git a/platform/uiq3/engine/debug.cpp b/platform/uiq3/engine/debug.cpp index c532187..81edbc5 100644 --- a/platform/uiq3/engine/debug.cpp +++ b/platform/uiq3/engine/debug.cpp @@ -2,6 +2,9 @@ #include // RDebug #include "debug.h" +//#define LOG_FILE "C:\\logs\\pico.log" +#define LOG_FILE _L("D:\\pico.log") + #ifdef __WINS__ void ExceptionHandler(TExcType exc) {} @@ -40,6 +43,7 @@ static const wchar_t * const exception_names[] = { }; +#if 0 static void getASpace(TUint *code_start, TUint *code_end, TUint *stack_start, TUint *stack_end) { TUint pc, sp; @@ -63,22 +67,24 @@ static void getASpace(TUint *code_start, TUint *code_end, TUint *stack_start, TU chunk.Close(); } } +#endif // tmp #if defined(__DEBUG_PRINT) -extern "C" char *debugString(); +extern "C" char *PDebugMain(); #endif // our very own exception handler void ExceptionHandler(TExcType exc) { + DEBUGPRINT(_L("ExceptionHandler() called!!!")); // this seems to never be called + +#if 0 TUint lr, sp, i; TUint stack_end = 0; // ending address of our stack chunk TUint code_start = 0, code_end = 0; // starting and ending addresses of our code chunk TUint guessed_address = 0; - DEBUGPRINT(_L("ExceptionHandler()")); // this seems to never be called - asm volatile ("str lr, %0" : "=m" (lr) ); asm volatile ("str sp, %0" : "=m" (sp) ); @@ -136,11 +142,11 @@ void ExceptionHandler(TExcType exc) // tmp #if defined(__DEBUG_PRINT) - char *ps, *cstr = debugString(); + char *ps, *cstr = PDebugMain(); for(ps = cstr; *ps; ps++) { if(*ps == '\n') { *ps = 0; - dprintf(cstr); + lprintf(cstr); cstr = ps+1; } } @@ -154,42 +160,47 @@ void ExceptionHandler(TExcType exc) // more descriptive replacement of "KERN-EXEC 3" panic buff1.Format(_L("K-EX3: %S"), &ptrExc); User::Panic(buff1, exc); +#endif } #endif // ifdef __WINS__ -#if defined(__DEBUG_PRINT) || defined(__WINS__) - -#ifndef __DLL__ - // c string dumper for RDebug::Print() - static TBuf<1024> sTextBuffer; - TDesC* DO_CONV(const char* s) - { - TPtrC8 text8((TUint8*) (s)); - sTextBuffer.Copy(text8); - return &sTextBuffer; - } -#endif - -#ifdef __DEBUG_PRINT_C +#if 1 // def __DEBUG_PRINT_C #include // va_* #include // vsprintf // debug print from c code - extern "C" void dprintf(char *format, ...) + extern "C" void lprintf(char *format, ...) { va_list args; char buffer[512]; + int len; va_start(args,format); - vsprintf(buffer,format,args); + len = vsprintf(buffer,format,args); va_end(args); + if (buffer[len-1] == '\n') + buffer[len-1] = 0; DEBUGPRINT(_L("%S"), DO_CONV(buffer)); } #endif + +#if defined(__DEBUG_PRINT) || defined(__WINS__) + +#ifndef __DLL__ + // c string dumper for RDebug::Print() + static TBuf<1024> sTextBuffer; + TDesC* DO_CONV(const char* s) + { + TPtrC8 text8((TUint8*) (s)); + sTextBuffer.Copy(text8); + return &sTextBuffer; + } +#endif + #ifdef __DEBUG_PRINT_FILE #include @@ -201,12 +212,12 @@ void ExceptionHandler(TExcType exc) { // try to open logMutex.CreateLocal(); - RFs fserv; + /*RFs fserv; fserv.Connect(); RFile logFile; - logFile.Replace(fserv, _L("C:\\logs\\pico.log"), EFileWrite|EFileShareAny); + logFile.Replace(fserv, LOG_FILE, EFileWrite|EFileShareAny); logFile.Close(); - fserv.Close(); + fserv.Close();*/ } // debug print to file @@ -225,7 +236,7 @@ void ExceptionHandler(TExcType exc) RThread thisThread; RFile logFile; - res = logFile.Open(fserv, _L("C:\\logs\\pico.log"), EFileWrite|EFileShareAny); + res = logFile.Open(fserv, LOG_FILE, EFileWrite|EFileShareAny); if(res) goto fail1; logFile.Size(size); logFile.Seek(ESeekStart, size); diff --git a/platform/uiq3/engine/debug.h b/platform/uiq3/engine/debug.h index bcd8287..8f7bebf 100644 --- a/platform/uiq3/engine/debug.h +++ b/platform/uiq3/engine/debug.h @@ -16,7 +16,7 @@ #ifdef __cplusplus extern "C" #endif - void dprintf(char *format, ...); + void lprintf(char *format, ...); #endif #else #define DEBUGPRINT(x...) diff --git a/platform/uiq3/engine/main.cpp b/platform/uiq3/engine/main.cpp index 93ef047..03c3e4f 100644 --- a/platform/uiq3/engine/main.cpp +++ b/platform/uiq3/engine/main.cpp @@ -17,14 +17,16 @@ #include "debug.h" #include "../Engine.h" -#include "../../../pico/picoInt.h" +#include +#include "../../common/emu.h" +#include "../emu.h" #include "vid.h" -#include "polledAS.h" +#include "PolledAS.h" //#include "audio.h" #include "audio_mediaserver.h" -#include -#include "../../../zlib/gzio_symb.h" +//#include +#include //#define BENCHMARK @@ -78,21 +80,20 @@ const char *actionNames[] = { // globals are allowed, so why not to (ab)use them? //TInt machineUid = 0; int gamestate = PGS_Paused, gamestate_next = PGS_Paused; -TPicoConfig *currentConfig = 0; -static char noticeMsg[64]; // notice msg to draw +char *loadrom_fname = NULL; +int loadrom_result = 0; static timeval noticeMsgTime = { 0, 0 }; // when started showing static CGameAudioMS *gameAudio = 0; // the audio object itself -static int reset_timing, pico_was_reset; -static int state_slot = 0; -extern const char *RomFileName; +static int reset_timing; +extern int pico_was_reset; extern RSemaphore initSemaphore; extern RSemaphore pauseSemaphore; +extern RSemaphore loadWaitSemaphore; // some forward declarations static void MainInit(); static void MainExit(); static void DumpMemInfo(); -void MainOldCleanup(); class TPicoDirectScreenAccess : public MDirectScreenAccess @@ -128,24 +129,8 @@ public: }; -static int snd_excess_add = 0, snd_excess_cnt = 0; // hack - -static void updateSound(void) +static void updateSound(int len) { - int len = PsndLen; - - snd_excess_cnt += snd_excess_add; - if (snd_excess_cnt >= 0x10000) { - snd_excess_cnt -= 0x10000; - if (PicoOpt&8) { - PsndOut[len*2] = PsndOut[len*2-2]; - PsndOut[len*2+1] = PsndOut[len*2-1]; - } else { - PsndOut[len] = PsndOut[len-1]; - } - len++; - } - PsndOut = gameAudio->NextFrameL(len); if(!PsndOut) { // sound output problems? strcpy(noticeMsg, "SOUND@OUTPUT@ERROR;@SOUND@DISABLED"); @@ -190,12 +175,16 @@ static void TargetEpocGameL() MainInit(); buff[0] = 0; + PicoInit(); + // just to keep the backlight on (works only on UIQ2) //blevent.Set(TRawEvent::EActive); // loop? - for(;;) { - if(gamestate == PGS_Running) { + for(;;) + { + if (gamestate == PGS_Running) + { // switch context to other thread User::After(50000); // prepare window and stuff @@ -217,15 +206,14 @@ static void TargetEpocGameL() if(!noticeMsgTime.tv_sec && pico_was_reset) gettimeofday(¬iceMsgTime, 0); - if (PsndOut) { - snd_excess_cnt = 0; - snd_excess_add = ((PsndRate - PsndLen*target_fps)<<16) / target_fps; - } + // prepare CD buffer + if (PicoAHW & PAHW_MCD) PicoCDBufferInit(); pico_was_reset = 0; reset_timing = 1; - while(gamestate == PGS_Running) { + while (gamestate == PGS_Running) + { gettimeofday(&tval, 0); if(reset_timing) { reset_timing = 0; @@ -242,7 +230,8 @@ static void TargetEpocGameL() } // second changed? - if(thissec != tval.tv_sec) { + if (thissec != tval.tv_sec) + { #ifdef BENCHMARK static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4]; if(++bench == 10) { @@ -254,14 +243,14 @@ static void TargetEpocGameL() bench_fps += frames_shown; sprintf(buff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2); #else - if(currentConfig->iFlags & 2) + if (currentConfig.EmuOpt & EOPT_SHOW_FPS) sprintf(buff, "%02i/%02i", frames_shown, frames_done); #endif thissec = tval.tv_sec; - if(PsndOut == 0 && currentConfig->iFrameskip >= 0) { + if(PsndOut == 0 && currentConfig.Frameskip >= 0) { frames_done = frames_shown = 0; } else { // it is quite common for this implementation to leave 1 fame unfinished @@ -278,8 +267,10 @@ static void TargetEpocGameL() lim_time = (frames_done+1) * target_frametime; - if(currentConfig->iFrameskip >= 0) { // frameskip enabled - for(i = 0; i < currentConfig->iFrameskip; i++) { + if (currentConfig.Frameskip >= 0) // frameskip enabled + { + for (i = 0; i < currentConfig.Frameskip && gamestate == PGS_Running; i++) + { CGameWindow::DoKeys(); SkipFrame(); frames_done++; if (PsndOut) { // do framelimitting if sound is enabled @@ -291,13 +282,17 @@ static void TargetEpocGameL() } lim_time += target_frametime; } - } else if(tval.tv_usec > lim_time) { // auto frameskip + } + else if(tval.tv_usec > lim_time) { // auto frameskip // no time left for this frame - skip CGameWindow::DoKeys(); SkipFrame(); frames_done++; continue; } + // we might have lost focus already + if (gamestate != PGS_Running) break; + CGameWindow::DoKeys(); PicoFrame(); @@ -306,7 +301,7 @@ static void TargetEpocGameL() if(thissec != tval.tv_sec) tval.tv_usec+=1000000; // sleep if we are still too fast - if(PsndOut != 0 || currentConfig->iFrameskip < 0) + if(PsndOut != 0 || currentConfig.Frameskip < 0) { // TODO: check if User::After() is accurate gettimeofday(&tval, 0); @@ -324,18 +319,36 @@ static void TargetEpocGameL() vidDrawFrame(notice, buff, frames_shown); frames_done++; frames_shown++; - } + } // while + + if (PicoAHW & PAHW_MCD) PicoCDBufferFree(); // save SRAM - if((currentConfig->iFlags & 1) && SRam.changed) { - saveLoadGame(0, 1); + if ((currentConfig.EmuOpt & EOPT_USE_SRAM) && SRam.changed) { + emu_SaveLoadGame(0, 1); SRam.changed = 0; } + CPolledActiveScheduler::Instance()->Schedule(); CGameWindow::FreeResources(); - } else if(gamestate == PGS_Paused) { + } + else if(gamestate == PGS_ReloadRom) + { + loadrom_result = emu_ReloadRom(loadrom_fname); + pico_was_reset = 1; + if (loadrom_result) + gamestate = PGS_Running; + else + gamestate = PGS_Paused; + DEBUGPRINT(_L("done loading ROM, retval=%i"), loadrom_result); + loadWaitSemaphore.Signal(); + User::After(50000); + } + else if(gamestate == PGS_Paused) { DEBUGPRINT(_L("pausing..")); pauseSemaphore.Wait(); - } else if(gamestate == PGS_KeyConfig) { + } + else if(gamestate == PGS_KeyConfig) + { // switch context to other thread User::After(50000); // prepare window and stuff @@ -364,6 +377,10 @@ static void TargetEpocGameL() } } + // this thread has to close it's own handles, + // other one will crash trying to do that + PicoExit(); + MainExit(); } @@ -373,9 +390,6 @@ static void MainInit() { DEBUGPRINT(_L("\r\n\r\nstarting..")); - // our thread might have been crashed previously, so many other objects may be still floating around - MainOldCleanup(); - DEBUGPRINT(_L("CPolledActiveScheduler::NewL()")); CPolledActiveScheduler::NewL(); // create Polled AS for the sound engine @@ -384,8 +398,7 @@ static void MainInit() DumpMemInfo(); // try to start pico - DEBUGPRINT(_L("PicoInit();")); - PicoInit(); + DEBUGPRINT(_L("PicoInit()")); PicoDrawSetColorFormat(2); PicoWriteSound = updateSound; @@ -403,13 +416,6 @@ static void MainExit() DEBUGPRINT(_L("%i: cleaning up.."), (TInt32) thisThread.Id()); - // save SRAM - if((currentConfig->iFlags & 1) && SRam.changed) { - saveLoadGame(0, 1); - SRam.changed = 0; - } - - PicoExit(); // pauseSemaphore.Close(); if(gameAudio) delete gameAudio; @@ -418,20 +424,6 @@ static void MainExit() delete CPolledActiveScheduler::Instance(); } -void MainOldCleanup() -{ - DEBUGPRINT(_L("MainOldCleanup..")); - - // There was previously a handle leak here, so thread stuff was not cleaned - // and I thought I would have to do it mself. - - // clean any resources which might be left after a thread crash - //CGameWindow::FreeResources(ETrue); - - //if(CPolledActiveScheduler::Instance()) - // delete CPolledActiveScheduler::Instance(); -} - static void DumpMemInfo() { TInt ramSize, ramSizeFree, romSize; @@ -444,12 +436,20 @@ static void DumpMemInfo() } +extern "C" TInt my_SetExceptionHandler(TInt, TExceptionHandler, TUint32); + TInt EmuThreadFunction(TAny*) { + TInt ret; const TUint32 exs = KExceptionAbort|KExceptionKill|KExceptionUserInterrupt|KExceptionFpe|KExceptionFault|KExceptionInteger|KExceptionDebug; - DEBUGPRINT(_L("EmuThreadFunction()")); - User::SetExceptionHandler(ExceptionHandler, exs/*(TUint32) -1*/); // does not work? + DEBUGPRINT(_L("EmuThreadFunction(), def ExceptionHandler %08x, my %08x"), + User::ExceptionHandler(), ExceptionHandler); + User::SetJustInTime(1); + ret = User::SetExceptionHandler(ExceptionHandler, exs/*(TUint32) -1*/); // does not work :( + // my_SetExceptionHandler(KCurrentThreadHandle, ExceptionHandler, 0xffffffff); + DEBUGPRINT(_L("SetExceptionHandler %i, %08x"), ret, User::ExceptionHandler()); + User::ModifyExceptionMask(0, exs); //TInt pc, sp; //asm volatile ("str pc, %0" : "=m" (pc) ); @@ -479,7 +479,7 @@ TInt EmuThreadFunction(TAny*) TRAPD(error, TargetEpocGameL()); - __ASSERT_ALWAYS(!error, User::Panic(_L("Picosmall"), error)); + __ASSERT_ALWAYS(!error, User::Panic(_L("PicoDrive"), error)); delete cleanup; DEBUGPRINT(_L("exitting..")); @@ -586,17 +586,19 @@ void CGameWindow::ConstructResourcesL() // try to start the audio engine static int PsndRate_old = 0, PicoOpt_old = 0, pal_old = 0; - if(gamestate == PGS_Running && (currentConfig->iFlags & 4)) { + if (gamestate == PGS_Running && (currentConfig.EmuOpt & EOPT_EN_SOUND)) + { TInt err = 0; if(PsndRate != PsndRate_old || (PicoOpt&11) != (PicoOpt_old&11) || Pico.m.pal != pal_old) { // if rate changed, reset all enabled chips, else reset only those chips, which were recently enabled //sound_reset(PsndRate != PsndRate_old ? PicoOpt : (PicoOpt&(PicoOpt^PicoOpt_old))); - sound_rerate(); + PsndRerate(1); } if(!gameAudio || PsndRate != PsndRate_old || ((PicoOpt&8) ^ (PicoOpt_old&8)) || Pico.m.pal != pal_old) { // rate or stereo or pal/ntsc changed if(gameAudio) delete gameAudio; gameAudio = 0; DEBUGPRINT(_L("starting audio: %i len: %i stereo: %i, pal: %i"), PsndRate, PsndLen, PicoOpt&8, Pico.m.pal); - TRAP(err, gameAudio = CGameAudioMS::NewL(PsndRate, (PicoOpt&8) ? 1 : 0, Pico.m.pal ? 50 : 60)); + TRAP(err, gameAudio = CGameAudioMS::NewL(PsndRate, (PicoOpt&8) ? 1 : 0, + Pico.m.pal ? 50 : 60, currentConfig.volume)); } if( gameAudio) { TRAP(err, PsndOut = gameAudio->ResumeL()); @@ -678,11 +680,6 @@ void CGameWindow::FreeResources() } vidFree(); - - // emu might change renderer by itself, so we may need to sync config - if(currentConfig && currentConfig->iPicoOpt != PicoOpt) { - currentConfig->iFlags |= 0x80; - } } @@ -708,7 +705,7 @@ void CGameWindow::DoKeys(void) const TPicoAreaConfigEntry *e = areaConfig + 1; for(i = 0; !e->rect.IsEmpty(); e++, i++) if(e->rect.Contains(p)) { - areaActions = currentConfig->iAreaBinds[i]; + areaActions = currentConfig.KeyBinds[i+256]; break; } //DEBUGPRINT(_L("pointer event: %i %i"), p.iX, p.iY); @@ -767,7 +764,7 @@ void CGameWindow::DoKeys(void) for(i = 9; i >= 0; i--) { int scan = pressedKeys[i]; if(scan) { - if(keyFlags[scan] & 1) allActions |= currentConfig->iKeyBinds[scan]; + if(keyFlags[scan] & 1) allActions |= currentConfig.KeyBinds[scan]; if((keyFlags[scan]& 3)==3) forceUpdate = 1; if(keyFlags[scan] & 2) keyFlags[scan] &= ~1; } @@ -807,7 +804,7 @@ void CGameWindow::DoKeysConfig(TUint &which) const TPicoAreaConfigEntry *e = areaConfig + 1; for(i = 0; e->rect != TRect(0,0,0,0); e++, i++) if(e->rect.Contains(p)) { - currentConfig->iAreaBinds[i] ^= currentActCode; + currentConfig.KeyBinds[i+256] ^= currentActCode; break; } } @@ -822,7 +819,7 @@ void CGameWindow::DoKeysConfig(TUint &which) if(which == 31) { gamestate = PGS_Paused; } else if (scan < 256) { - if(!(keyFlags[scan]&0x40)) currentConfig->iKeyBinds[scan] ^= currentActCode; + if(!(keyFlags[scan]&0x40)) currentConfig.KeyBinds[scan] ^= currentActCode; } } @@ -844,22 +841,22 @@ void CGameWindow::DoKeysConfig(TUint &which) void CGameWindow::RunEvents(TUint32 which) { - if(which & 0x4000) currentConfig->iFrameskip = -1; - if(which & 0x2000) currentConfig->iFrameskip = 8; - if(which & 0x1800) { // save or load (but not both) + if (which & 0x4000) currentConfig.Frameskip = -1; + if (which & 0x2000) currentConfig.Frameskip = 8; + if (which & 0x1800) { // save or load (but not both) if(PsndOut) gameAudio->Pause(); // this may take a while, so we pause sound output vidDrawNotice((which & 0x1000) ? "LOADING@GAME" : "SAVING@GAME"); - saveLoadGame(which & 0x1000); + emu_SaveLoadGame(which & 0x1000, 0); if(PsndOut) PsndOut = gameAudio->ResumeL(); reset_timing = 1; } - if(which & 0x0400) gamestate = PGS_Paused; - if(which & 0x0200) { // switch renderer - if(!(currentConfig->iScreenMode == TPicoConfig::PMFit && - (currentConfig->iScreenRotation == TPicoConfig::PRot0 || currentConfig->iScreenRotation == TPicoConfig::PRot180))) { - + if (which & 0x0400) gamestate = PGS_Paused; + if (which & 0x0200) { // switch renderer + if (!(currentConfig.scaling == TPicoConfig::PMFit && + (currentConfig.rotation == TPicoConfig::PRot0 || currentConfig.rotation == TPicoConfig::PRot180))) + { PicoOpt^=0x10; vidInit(0, 1); @@ -878,111 +875,20 @@ void CGameWindow::RunEvents(TUint32 which) sprintf(noticeMsg, "SAVE@SLOT@%i@SELECTED", state_slot); gettimeofday(¬iceMsgTime, 0); } - if(which & 0x0020) if(gameAudio) gameAudio->ChangeVolume(0); - if(which & 0x0010) if(gameAudio) gameAudio->ChangeVolume(1); + if(which & 0x0020) if(gameAudio) currentConfig.volume = gameAudio->ChangeVolume(0); + if(which & 0x0010) if(gameAudio) currentConfig.volume = gameAudio->ChangeVolume(1); } -// must use wrappers, or else will run into some weird loader error (see pico/area.c) -static size_t fRead2(void *p, size_t _s, size_t _n, void *file) -{ - return fread(p, _s, _n, (FILE *) file); -} - -static size_t fWrite2(void *p, size_t _s, size_t _n, void *file) -{ - return fwrite(p, _s, _n, (FILE *) file); -} - -static size_t gzRead2(void *p, size_t, size_t _n, void *file) -{ - return gzread(file, p, _n); -} - -static size_t gzWrite2(void *p, size_t, size_t _n, void *file) +extern "C" void emu_noticeMsgUpdated(void) { - return gzwrite(file, p, _n); -} - - -// this function is shared between both threads -int saveLoadGame(int load, int sram) -{ - int res = 0; - - if(!RomFileName) return -1; - - // make save filename - char saveFname[KMaxFileName]; - strcpy(saveFname, RomFileName); - saveFname[KMaxFileName-8] = 0; - if(saveFname[strlen(saveFname)-4] == '.') saveFname[strlen(saveFname)-4] = 0; - if(sram) strcat(saveFname, ".srm"); - else { - if(state_slot > 0 && state_slot < 10) sprintf(saveFname, "%s.%i", saveFname, state_slot); - strcat(saveFname, ".mds"); - } - - DEBUGPRINT(_L("saveLoad (%i, %i): %S"), load, sram, DO_CONV(saveFname)); - - if(sram) { - FILE *sramFile; - int sram_size = SRam.end-SRam.start+1; - if(SRam.reg_back & 4) sram_size=0x2000; - if(!SRam.data) return 0; // SRam forcefully disabled for this game - if(load) { - sramFile = fopen(saveFname, "rb"); - if(!sramFile) return -1; - fread(SRam.data, 1, sram_size, sramFile); - fclose(sramFile); - } 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, "wb"); - res = fwrite(SRam.data, 1, sram_size, sramFile); - res = (res != sram_size) ? -1 : 0; - fclose(sramFile); - } - } - return res; - } else { - void *PmovFile = NULL; - // try gzip first - if(currentConfig->iFlags & 0x80) { - strcat(saveFname, ".gz"); - if( (PmovFile = gzopen(saveFname, load ? "rb" : "wb")) ) { - areaRead = gzRead2; - areaWrite = gzWrite2; - if(!load) gzsetparams(PmovFile, 9, Z_DEFAULT_STRATEGY); - } else - saveFname[strlen(saveFname)-3] = 0; - } - if(!PmovFile) { // gzip failed or was disabled - if( (PmovFile = fopen(saveFname, load ? "rb" : "wb")) ) { - areaRead = fRead2; - areaWrite = fWrite2; - } - } - if(PmovFile) { - PmovState(load ? 6 : 5, PmovFile); // load/save - strcpy(noticeMsg, load ? "GAME@LOADED" : "GAME@SAVED"); - if(areaRead == gzRead2) - gzclose(PmovFile); - else fclose ((FILE *) PmovFile); - PmovFile = 0; - if (load) Pico.m.dirtyPal=1; - } else { - strcpy(noticeMsg, load ? "LOAD@FAILED" : "SAVE@FAILED"); - res = -1; - } - - gettimeofday(¬iceMsgTime, 0); - return res; + char *p = noticeMsg; + while (*p) { + if (*p == ' ') *p = '@'; + if (*p < '0' || *p > 'Z') { *p = 0; break; } + p++; } + gettimeofday(¬iceMsgTime, 0); } // static class members diff --git a/platform/uiq3/engine/vid.cpp b/platform/uiq3/engine/vid.cpp index 308b10b..bd1b3fd 100644 --- a/platform/uiq3/engine/vid.cpp +++ b/platform/uiq3/engine/vid.cpp @@ -5,20 +5,20 @@ #include "vid.h" #include "../Engine.h" -#include "../../../pico/picoInt.h" +#include +#include "../../common/emu.h" #include "blit.h" #include "debug.h" // global stuff -extern TPicoConfig *currentConfig; extern TPicoAreaConfigEntry areaConfig[]; extern const char *actionNames[]; // main framebuffer static void *screenbuff = 0; // pointer to real device video memory //static -extern "C" { unsigned char *framebuff = 0; } // temporary buffer +extern "C" { unsigned char *PicoDraw2FB = 0; } // temporary buffer const int framebuffsize = (8+320)*(8+240+8)*2+8*2; // actual framebuffer size (in bytes+to support new rendering mode) // drawer function pointers @@ -87,35 +87,25 @@ static const unsigned long mask_numbers[] = { }; -//////////////////////////////// -// Cram functions - -static int EmuCramNull(int cram) -{ - User::Panic(_L("Cram called!!"), 0); - return cram; -} - - //////////////////////////////// // PicoScan functions -static int EmuScan8(unsigned int num, void *sdata) +static int EmuScanBegin8(unsigned int num) { - DrawLineDest = framebuff + 328*(num+1) + 328*8 + 8; + DrawLineDest = PicoDraw2FB + 328*num + 328*8 + 8; return 0; } -static int EmuScanFit0(unsigned int num, void *sdata) +static int EmuScanEndFit0(unsigned int num) { // 0.75, 168 lines static int u = 0, num2 = 0; if(!num) u = num2 = 0; - DrawLineDest = framebuff + 328*(++num2) + 328*8 + 8; + DrawLineDest = PicoDraw2FB + 328*(++num2) + 328*8 + 8; u += 6666; @@ -136,7 +126,7 @@ static int EmuScanFit0(unsigned int num, void *sdata) static void drawTextM2(int x, int y, const char *text) { - unsigned char *vidmem = framebuff + 328*8 + 8; + unsigned char *vidmem = PicoDraw2FB + 328*8 + 8; int charmask, i, cx = x, cy; unsigned char *l, *le; @@ -164,7 +154,7 @@ static void drawTextM2(int x, int y, const char *text) static void drawTextM2Fat(int x, int y, const char *text) { - unsigned char *vidmem = framebuff + 328*8 + 8; + unsigned char *vidmem = PicoDraw2FB + 328*8 + 8; int charmask, i, cx = x&~1, cy; unsigned short *l, *le; @@ -257,7 +247,7 @@ static void fillLocalPal(void) vidConvCpyRGB32(localPal, Pico.cram, 0x40); vidConvCpyRGB32sh(localPal+0x40, Pico.cram, 0x40); vidConvCpyRGB32hi(localPal+0x80, Pico.cram, 0x40); - blockcpy(localPal+0xc0, localPal+0x40, 0x40*4); + memcpy32(localPal+0xc0, localPal+0x40, 0x40); localPal[0xe0] = 0x00000000; // reserved pixels for OSD localPal[0xf0] = 0x00ee0000; } else if (rendstatus & 0x20) { // mid-frame palette changes @@ -273,7 +263,7 @@ static void fillLocalPal(void) // note: the internal 8 pixel border is taken care by asm code static void vidBlit_90(int full) { - unsigned char *ps = framebuff+328*8; + unsigned char *ps = PicoDraw2FB+328*8; unsigned long *pd = (unsigned long *) screenbuff; if (Pico.m.dirtyPal) fillLocalPal(); @@ -291,7 +281,7 @@ static void vidBlit_90(int full) static void vidBlit_270(int full) { - unsigned char *ps = framebuff+328*8; + unsigned char *ps = PicoDraw2FB+328*8; unsigned long *pd = (unsigned long *) screenbuff; if (Pico.m.dirtyPal) fillLocalPal(); @@ -310,7 +300,7 @@ static void vidBlit_270(int full) static void vidBlitCenter_0(int full) { - unsigned char *ps = framebuff+328*8+8; + unsigned char *ps = PicoDraw2FB+328*8+8; unsigned long *pd = (unsigned long *) screenbuff; if (Pico.m.dirtyPal) fillLocalPal(); @@ -323,7 +313,7 @@ static void vidBlitCenter_0(int full) static void vidBlitCenter_180(int full) { - unsigned char *ps = framebuff+328*8+8; + unsigned char *ps = PicoDraw2FB+328*8+8; unsigned long *pd = (unsigned long *) screenbuff; if (Pico.m.dirtyPal) fillLocalPal(); @@ -339,8 +329,8 @@ static void vidBlitFit_0(int full) if (Pico.m.dirtyPal) fillLocalPal(); if(Pico.video.reg[12]&1) - vidConvCpy_center2_40c_0(screenbuff, framebuff+328*8, localPal, 168); - else vidConvCpy_center2_32c_0(screenbuff, framebuff+328*8, localPal, 168); + vidConvCpy_center2_40c_0(screenbuff, PicoDraw2FB+328*8, localPal, 168); + else vidConvCpy_center2_32c_0(screenbuff, PicoDraw2FB+328*8, localPal, 168); if(full) vidClear((unsigned long *)screenbuff + 168*256, 320-168); } @@ -350,8 +340,8 @@ static void vidBlitFit_180(int full) if (Pico.m.dirtyPal) fillLocalPal(); if(Pico.video.reg[12]&1) - vidConvCpy_center2_40c_180(screenbuff, framebuff+328*8, localPal, 168); - else vidConvCpy_center2_32c_180(screenbuff, framebuff+328*8-64, localPal, 168); + vidConvCpy_center2_40c_180(screenbuff, PicoDraw2FB+328*8, localPal, 168); + else vidConvCpy_center2_32c_180(screenbuff, PicoDraw2FB+328*8-64, localPal, 168); if(full) vidClear((unsigned long *)screenbuff + 168*256, 320-168); } @@ -361,8 +351,8 @@ static void vidBlitFit2_0(int full) if (Pico.m.dirtyPal) fillLocalPal(); if(Pico.video.reg[12]&1) - vidConvCpy_center2_40c_0(screenbuff, framebuff+328*8, localPal, 224); - else vidConvCpy_center2_32c_0(screenbuff, framebuff+328*8, localPal, 224); + vidConvCpy_center2_40c_0(screenbuff, PicoDraw2FB+328*8, localPal, 224); + else vidConvCpy_center2_32c_0(screenbuff, PicoDraw2FB+328*8, localPal, 224); if(full) vidClear((unsigned long *)screenbuff + 224*256, 96); } @@ -372,15 +362,15 @@ static void vidBlitFit2_180(int full) if (Pico.m.dirtyPal) fillLocalPal(); if(Pico.video.reg[12]&1) - vidConvCpy_center2_40c_180(screenbuff, framebuff+328*8, localPal, 224); - else vidConvCpy_center2_32c_180(screenbuff, framebuff+328*8-64, localPal, 224); + vidConvCpy_center2_40c_180(screenbuff, PicoDraw2FB+328*8, localPal, 224); + else vidConvCpy_center2_32c_180(screenbuff, PicoDraw2FB+328*8-64, localPal, 224); if(full) vidClear((unsigned long *)screenbuff + 224*256, 96); } static void vidBlitCfg(void) { - unsigned short *ps = (unsigned short *) framebuff; + unsigned short *ps = (unsigned short *) PicoDraw2FB; unsigned long *pd = (unsigned long *) screenbuff; int i; @@ -402,20 +392,17 @@ int vidInit(void *vidmem, int reinit) if(!reinit) { // prepare framebuffer screenbuff = vidmem; - framebuff = (unsigned char *) malloc(framebuffsize); + PicoDraw2FB = (unsigned char *) malloc(framebuffsize); if(!screenbuff) return KErrNotSupported; - if(!framebuff) return KErrNoMemory; - - memset(framebuff, 0, framebuffsize); + if(!PicoDraw2FB) return KErrNoMemory; - // Cram function: go and hack Pico so it never gets called - PicoCram = EmuCramNull; + memset(PicoDraw2FB, 0, framebuffsize); } // select suitable blitters vidBlit = vidBlit_270; - PicoScan = EmuScan8; + PicoScanBegin = EmuScanBegin8; drawTextFps = drawTextFps0; drawTextNotice = drawTextNotice0; @@ -424,12 +411,13 @@ int vidInit(void *vidmem, int reinit) localPal[0xf0] = 0x00ee0000; // setup all orientation related stuff - if(currentConfig->iScreenRotation == TPicoConfig::PRot0) { - if(currentConfig->iScreenMode == TPicoConfig::PMCenter) { + if (currentConfig.rotation == TPicoConfig::PRot0) + { + if (currentConfig.scaling == TPicoConfig::PMCenter) { vidBlit = vidBlitCenter_0; drawTextFps = drawTextFpsCenter0; drawTextNotice = drawTextNoticeCenter0; - } else if(currentConfig->iScreenMode == TPicoConfig::PMFit2) { + } else if (currentConfig.scaling == TPicoConfig::PMFit2) { vidBlit = vidBlitFit2_0; drawTextFps = drawTextFpsFit2_0; drawTextNotice = drawTextNoticeFit2_0; @@ -437,16 +425,20 @@ int vidInit(void *vidmem, int reinit) vidBlit = vidBlitFit_0; drawTextFps = drawTextFpsFit0; drawTextNotice = drawTextNoticeFit0; - PicoScan = EmuScanFit0; + PicoScanEnd = EmuScanEndFit0; } - } else if(currentConfig->iScreenRotation == TPicoConfig::PRot90) { + } else if (currentConfig.rotation == TPicoConfig::PRot90) { vidBlit = vidBlit_90; - } else if(currentConfig->iScreenRotation == TPicoConfig::PRot180) { - if(currentConfig->iScreenMode == TPicoConfig::PMCenter) { + } + else if (currentConfig.rotation == TPicoConfig::PRot180) + { + if (currentConfig.scaling == TPicoConfig::PMCenter) + { vidBlit = vidBlitCenter_180; drawTextFps = drawTextFpsCenter0; drawTextNotice = drawTextNoticeCenter0; - } else if(currentConfig->iScreenMode == TPicoConfig::PMFit2) { + } + else if (currentConfig.scaling == TPicoConfig::PMFit2) { vidBlit = vidBlitFit2_180; drawTextFps = drawTextFpsFit2_0; drawTextNotice = drawTextNoticeFit2_0; @@ -454,9 +446,10 @@ int vidInit(void *vidmem, int reinit) vidBlit = vidBlitFit_180; drawTextFps = drawTextFpsFit0; drawTextNotice = drawTextNoticeFit0; - PicoScan = EmuScanFit0; + PicoScanEnd = EmuScanEndFit0; } - } else if(currentConfig->iScreenRotation == TPicoConfig::PRot270) { + } + else if (currentConfig.rotation == TPicoConfig::PRot270) { vidBlit = vidBlit_270; } @@ -469,16 +462,16 @@ int vidInit(void *vidmem, int reinit) void vidFree() { - free(framebuff); - framebuff = 0; + free(PicoDraw2FB); + PicoDraw2FB = 0; } void vidDrawFrame(char *noticeStr, char *fpsStr, int num) { - DrawLineDest = framebuff + 328*8 + 8; + DrawLineDest = PicoDraw2FB + 328*8 + 8; // PicoFrame(); // moved to main loop - if(currentConfig->iFlags & 2) + if (currentConfig.EmuOpt & EOPT_SHOW_FPS) drawTextFps(fpsStr); drawTextNotice(noticeStr); @@ -489,7 +482,7 @@ void vidDrawFrame(char *noticeStr, char *fpsStr, int num) static void drawText0(int x, int y, const char *text, long color) { - unsigned short *vidmem=(unsigned short *)framebuff; + unsigned short *vidmem=(unsigned short *)PicoDraw2FB; int charmask, i, cx = x, cy; unsigned short *l, *le, dmask=0x0333; @@ -519,7 +512,7 @@ static void drawText0(int x, int y, const char *text, long color) // draws rect with width - 1 and height - 1 static void drawRect(const TRect &rc, unsigned short color) { - unsigned short *vidmem=(unsigned short *)framebuff; + unsigned short *vidmem=(unsigned short *)PicoDraw2FB; if(rc.iTl.iX - rc.iBr.iX && rc.iTl.iY - rc.iBr.iY) { int stepX = rc.iTl.iX < rc.iBr.iX ? 1 : -1; @@ -540,7 +533,7 @@ static void drawRect(const TRect &rc, unsigned short color) // draws fullsize filled rect static void drawRectFilled(const TRect rc, unsigned short color) { - unsigned short *vidmem=(unsigned short *)framebuff; + unsigned short *vidmem=(unsigned short *)PicoDraw2FB; if(rc.iTl.iX - rc.iBr.iX && rc.iTl.iY - rc.iBr.iY) { int stepX = rc.iTl.iX < rc.iBr.iX ? 1 : -1; @@ -559,7 +552,7 @@ static void drawRectFilled(const TRect rc, unsigned short color) // direction: -1 left, 1 right static void drawArrow0(TPoint p, int direction, unsigned short color) { - unsigned short *vidmem=(unsigned short *)framebuff; + unsigned short *vidmem=(unsigned short *)PicoDraw2FB; int width = 15; int x = p.iX; int y = p.iY; @@ -603,7 +596,7 @@ void vidKeyConfigFrame(const TUint whichAction) int i; char buttonNames[128]; buttonNames[0] = 0; - memset(framebuff, 0, framebuffsize); + memset(PicoDraw2FB, 0, framebuffsize); unsigned long currentActCode = 1 << whichAction; @@ -611,7 +604,7 @@ void vidKeyConfigFrame(const TUint whichAction) const TPicoAreaConfigEntry *e = areaConfig + 1; i = 0; while(e->rect != TRect(0,0,0,0)) { e++; i++; } for(e--, i--; e->rect != TRect(0,0,0,0); e--, i--) - drawRect(e->rect, (currentConfig->iAreaBinds[i] & currentActCode) ? color_red : color_red_dim); + drawRect(e->rect, (currentConfig.KeyBinds[i+256] & currentActCode) ? color_red : color_red_dim); // action name control drawRectFilled(TRect(72, 2, 168, 20), color_grey); // 96x14 @@ -621,14 +614,14 @@ void vidKeyConfigFrame(const TUint whichAction) drawText0(86, 9, actionNames[whichAction], color_red); // draw active button names if there are any - for(i = 0; i < 256; i++) { - if(currentConfig->iKeyBinds[i] & currentActCode) { + for (i = 0; i < 256; i++) { + if (currentConfig.KeyBinds[i] & currentActCode) { if(buttonNames[0]) strcat(buttonNames, ";@"); strcat(buttonNames, vidGetScanName(i)); } } - if(buttonNames[0]) { + if (buttonNames[0]) { buttonNames[61] = 0; // only 60 chars fit drawText0(6, 48, buttonNames, color_blue); } @@ -638,7 +631,7 @@ void vidKeyConfigFrame(const TUint whichAction) void vidDrawNotice(const char *txt) { - if(framebuff) { + if(PicoDraw2FB) { drawTextNotice(txt); vidBlit(1); } diff --git a/platform/uiq3/makezip.cmd b/platform/uiq3/makezip.cmd deleted file mode 100644 index 2aea772..0000000 --- a/platform/uiq3/makezip.cmd +++ /dev/null @@ -1,3 +0,0 @@ -@cd _out -@"C:\Program Files\arch\WinRAR\WinRAR.exe" a PicoDrive.zip PicoDrive.SIS config.txt ..\..\readme.txt -@cd.. diff --git a/platform/uiq3/PicoDrive.hrh b/platform/uiq3/picodrive.hrh similarity index 83% rename from platform/uiq3/PicoDrive.hrh rename to platform/uiq3/picodrive.hrh index 910fde8..445c7fb 100644 --- a/platform/uiq3/PicoDrive.hrh +++ b/platform/uiq3/picodrive.hrh @@ -31,6 +31,7 @@ enum TAppMenuCommands // pages ECtlOptPageMain, ECtlOptPageSound, + ECtlOptPageMCD, ECtlOptPageMisc, // main page ECtlOptRotationLabel, @@ -45,8 +46,8 @@ enum TAppMenuCommands ECtlOptScreenModeFit, ECtlOptScreenModeFit2, ECtlOptUseAltRend, - ECtlOptUseAccTiming, - ECtlOptUseAccSprites, +// ECtlOptUseAccTiming, +// ECtlOptUseAccSprites, ECtlOptShowFPS, // sound page ECtlOptEnableSound, @@ -56,6 +57,13 @@ enum TAppMenuCommands ECtlOptEmulateSN76496, ECtlOptSndQLabel, ECtlOptSndQuality, + // MCD page + ECtlOptCDleds, + ECtlOptCDcdda, + ECtlOptCDpcm, + ECtlOptCDramcart, + ECtlOptCDscalerot, + ECtlOptCDbettersync, // misc page ECtlOpt6ButtonPad, ECtlOptGzipStates, diff --git a/platform/uiq3/picodrive.pkg b/platform/uiq3/picodrive.pkg new file mode 100644 index 0000000..c3e2de1 --- /dev/null +++ b/platform/uiq3/picodrive.pkg @@ -0,0 +1,9 @@ +%{"notaz"} +:"notaz" +#{"PicoDrive"}, (0xA00010F3), 1, 51, 0, TYPE=SA +(0x101F6300), 3, 0, 0, {"UIQ30ProductID"} +"picodrive.exe"-"!:\sys\bin\PicoDrive.exe" +"rsc/picodrive.rsc"-"!:\resource\apps\PicoDrive.rsc" +"picodrive.mbm"-"!:\resource\apps\PicoDrive.mbm" +"rsc/picodrive_loc.rsc"-"!:\resource\apps\PicoDrive_loc.rsc" +"rsc/picodrive_reg.rsc"-"!:\private\10003a3f\import\apps\PicoDrive_reg.rsc" diff --git a/platform/uiq3/port_config.h b/platform/uiq3/port_config.h index 79f5fda..0686c26 100644 --- a/platform/uiq3/port_config.h +++ b/platform/uiq3/port_config.h @@ -3,21 +3,32 @@ #ifndef PORT_CONFIG_H #define PORT_CONFIG_H +#define CASE_SENSITIVE_FS 0 +#define DONT_OPEN_MANY_FILES 0 +#define REDUCE_IO_CALLS 0 +#define SIMPLE_WRITE_SOUND 0 + +// draw.c +#define OVERRIDE_HIGHCOL 0 + // draw2.c #define START_ROW 0 // which row of tiles to start rendering at? #define END_ROW 28 // ..end // pico.c -#define CAN_HANDLE_240_LINES 0 // fow now - -//#define dprintf(f,...) printf(f"\n",##__VA_ARGS__) -#ifdef __DEBUG_PRINT -#ifdef __cplusplus -extern "C" -#endif -void dprintf(char *format, ...); -#else +#define CAN_HANDLE_240_LINES 0 // for now + +// logging emu events +#define EL_LOGMASK (EL_STATUS|EL_IDLE) // (EL_STATUS|EL_ANOMALY|EL_UIO|EL_SRAMIO|EL_INTS|EL_CDPOLL) // xffff + +//extern void dprintf(char *format, ...); +//#define dprintf(f,...) printf("%05i:%03i: " f "\n",Pico.m.frame_count,Pico.m.scanline,##__VA_ARGS__) #define dprintf(x...) -#endif -#endif //PORT_CONFIG_H +// platform +#define PLAT_MAX_KEYS (256+19) +#define PLAT_HAVE_JOY 0 +#define PATH_SEP "\\" +#define PATH_SEP_C '\\' + +#endif // PORT_CONFIG_H diff --git a/platform/uiq3/port_config.s b/platform/uiq3/port_config.s index bc0f97f..3361f1a 100644 --- a/platform/uiq3/port_config.s +++ b/platform/uiq3/port_config.s @@ -1,8 +1,13 @@ -@ .equiv START_ROW, 1 -@ .equiv END_ROW, 27 +@ vim:filetype=armasm + +@ .equiv START_ROW, 1 +@ .equiv END_ROW, 27 @ one row means 8 pixels. If above example was used, (27-1)*8=208 lines would be rendered. -.equiv START_ROW, 0 -.equiv END_ROW, 28 +.equiv START_ROW, 0 +.equiv END_ROW, 28 + +.equiv OVERRIDE_HIGHCOL, 0 +.equiv UNALIGNED_DRAWLINEDEST, 0 @ this should be set to one only for GP2X port -.equiv EXTERNAL_YM2612, 0 +.equiv EXTERNAL_YM2612, 0 diff --git a/platform/uiq3/qconn.cmd b/platform/uiq3/qconn.cmd deleted file mode 100644 index 429b196..0000000 --- a/platform/uiq3/qconn.cmd +++ /dev/null @@ -1 +0,0 @@ -@..\..\..\qconsole-1.60\qtty-1.60\release\qtty --qc-addr M600i --qc-channel 5 --user qconsole --pass server \ No newline at end of file diff --git a/platform/uiq3/qlog.cmd b/platform/uiq3/qlog.cmd deleted file mode 100644 index c9e1542..0000000 --- a/platform/uiq3/qlog.cmd +++ /dev/null @@ -1 +0,0 @@ -@..\..\..\qconsole-1.60\qtty-1.60\release\qtty --qc-addr M600i --qc-channel 5 --user qconsole --pass server --cmds "cat c:\logs\pico.log" exit diff --git a/platform/uiq3/qup.cmd b/platform/uiq3/qup.cmd deleted file mode 100644 index de5594a..0000000 --- a/platform/uiq3/qup.cmd +++ /dev/null @@ -1 +0,0 @@ -@..\..\..\qconsole-1.60\qtty-1.60\release\qtty --qc-addr M600i --qc-channel 5 --user qconsole --pass server --cmds "put c:\Shared\PicoDrive.SIS _out\PicoDrive.SIS" "rundoc c:\Shared\PicoDrive.SIS" exit diff --git a/platform/uiq3/rsc/PicoDrive_loc.rss b/platform/uiq3/rsc/PicoDrive_loc.rss deleted file mode 100644 index 2495e0b..0000000 --- a/platform/uiq3/rsc/PicoDrive_loc.rss +++ /dev/null @@ -1,20 +0,0 @@ -#include - -// This file localise the applications icons and caption -RESOURCE LOCALISABLE_APP_INFO - { - caption_and_icon = - { - CAPTION_AND_ICON_INFO - { - caption = "PicoDrive"; - // Icons are used to represent applications in the - // application launcher and application title bar. - // The number_of_icons value identifies how many icons - // that exist in the icon_file. - number_of_icons = 3; - // Using the application icons. - icon_file = "\\Resource\\Apps\\PicoDrive.mbm"; - } - }; - } diff --git a/platform/uiq3/rsc/PicoDrive.rss b/platform/uiq3/rsc/picodrive.rss similarity index 88% rename from platform/uiq3/rsc/PicoDrive.rss rename to platform/uiq3/rsc/picodrive.rss index 6bbef7c..eb5f5a6 100644 --- a/platform/uiq3/rsc/PicoDrive.rss +++ b/platform/uiq3/rsc/picodrive.rss @@ -3,7 +3,7 @@ NAME PCDR #include #include #include -#include +#include #include "picodrive.hrh" @@ -89,7 +89,7 @@ RESOURCE QIK_COMMAND_LIST r_app_commands QIK_COMMAND { id=EEikCmdPicoSettings; text="Settings"; type=EQikCommandTypeScreen; namedGroupId=EEikCmdPicoConfig; }, QIK_COMMAND { id=EEikCmdHelpAbout; text="About"; type=EQikCommandTypeScreen; namedGroupId=EEikCmdPicoConfig; }, - QIK_COMMAND { id=EEikCmdPicoLoadROM; text="Load new ROM"; type=EQikCommandTypeScreen; groupId=EEikCmdPicoResume; }, + QIK_COMMAND { id=EEikCmdPicoLoadROM; text="Load new ROM/ISO";type=EQikCommandTypeScreen; groupId=EEikCmdPicoResume; }, QIK_COMMAND { id=EEikCmdPicoReset; text="Reset game"; type=EQikCommandTypeScreen; groupId=EEikCmdPicoResume; }, QIK_COMMAND { id=EEikCmdPicoLoadState; text="Load state"; type=EQikCommandTypeScreen; groupId=EEikCmdPicoResume; }, QIK_COMMAND { id=EEikCmdPicoSaveState; text="Save state"; type=EQikCommandTypeScreen; groupId=EEikCmdPicoResume; }, @@ -137,6 +137,12 @@ RESOURCE ARRAY r_pico_config_pages lines = r_pico_config_page_sound; }, PAGE + { + id = ECtlOptPageMCD; + text = "MegaCD"; + lines = r_pico_config_page_mcd; + }, + PAGE { id = ECtlOptPageMisc; text = "Misc"; @@ -151,7 +157,7 @@ RESOURCE ARRAY r_pico_config_page_main items = { DLG_LINE { - id = ECtlOptRotationLabel; + id = ECtlOptRotationLabel; type = EEikCtLabel; prompt = "Screen Rotation"; control = LABEL { horiz_align = EEikLabelAlignHLeft; }; @@ -167,7 +173,7 @@ RESOURCE ARRAY r_pico_config_page_main }, DLG_LINE { - id = ECtlOptScreenModeLabel; + id = ECtlOptScreenModeLabel; type = EEikCtLabel; prompt = "Screen Mode"; control = LABEL { horiz_align = EEikLabelAlignHLeft; }; @@ -188,18 +194,6 @@ RESOURCE ARRAY r_pico_config_page_main prompt = "Fast renderer (inaccurate)"; }, DLG_LINE - { - id = ECtlOptUseAccTiming; - type = EEikCtCheckBox; - prompt = "Accurate timing (slower)"; - }, - DLG_LINE - { - id = ECtlOptUseAccSprites; - type = EEikCtCheckBox; - prompt = "Accurate sprites (slower)"; - }, - DLG_LINE { id = ECtlOptShowFPS; type = EEikCtCheckBox; @@ -220,7 +214,7 @@ RESOURCE ARRAY r_pico_config_page_sound }, DLG_LINE { - id = ECtlOptChipSelLabel; + id = ECtlOptChipSelLabel; type = EEikCtLabel; prompt = "Emulate these sound chips:"; control = LABEL { horiz_align = EEikLabelAlignHLeft; }; @@ -245,7 +239,7 @@ RESOURCE ARRAY r_pico_config_page_sound }, DLG_LINE { - id = ECtlOptSndQLabel; + id = ECtlOptSndQLabel; type = EEikCtLabel; prompt = "Quality (lowest is fastest)"; control = LABEL { horiz_align = EEikLabelAlignHLeft; }; @@ -262,6 +256,43 @@ RESOURCE ARRAY r_pico_config_page_sound } +RESOURCE ARRAY r_pico_config_page_mcd +{ + items = { + DLG_LINE + { + id = ECtlOptCDcdda; + type = EEikCtCheckBox; + prompt = "CDDA audio"; + }, + DLG_LINE + { + id = ECtlOptCDpcm; + type = EEikCtCheckBox; + prompt = "PCM audio"; + }, + DLG_LINE + { + id = ECtlOptCDramcart; + type = EEikCtCheckBox; + prompt = "SaveRAM cart"; + }, + DLG_LINE + { + id = ECtlOptCDscalerot; + type = EEikCtCheckBox; + prompt = "Scale/Rot. fx (slow)"; + }, + DLG_LINE + { + id = ECtlOptCDbettersync; + type = EEikCtCheckBox; + prompt = "Better sync (slow)"; + } + }; +} + + RESOURCE ARRAY r_pico_config_page_misc { items = { @@ -449,7 +480,7 @@ RESOURCE TBUF r_pico_text_about "

Version %S, by notaz."\ "

Port based on UIQ2 version, which is based on PicoDrive 0.030 for Pocket PC by Dave"\ "

Email: notasas@gmail.com"\ - "

Web: http://notaz.atspace.com"\ + "

Web: http://notaz.gp2x.de"\ "

Dave's Web: http://www.finalburn.com"; } @@ -557,17 +588,22 @@ RESOURCE ARRAY r_pico_tbuf_credits LBUF{txt="- DrZ80, the Z80 emulator written in ARM assembly.";}, LBUF{txt="Homepage: http://reesy.gp32x.de/";}, LBUF{txt="";}, - LBUF{txt="Tatsuyuki Satoh, Jarek Burczynski, MultiArcadeMachineEmulator (MAME) development";}, + LBUF{txt="MultiArcadeMachineEmulator (MAME) development";}, LBUF{txt="- software implementation of Yamaha FM sound generator and";}, LBUF{txt="Texas Instruments SN76489 / SN76496 programmable tone / noise generator";}, LBUF{txt="Homepage: http://www.mame.net/";}, LBUF{txt="";}, + LBUF{txt="Helix community";}, + LBUF{txt="Helix mp3 decoder";}, + LBUF{txt="";}, LBUF{txt="Additional thanks:";}, LBUF{txt="- Charles MacDonald (http://cgfm2.emuviews.com/) for old but still very useful info about genesis hardware.";}, LBUF{txt="- Stéphane Dallongeville for creating Gens and making it open-source.";}, LBUF{txt="- Steve Snake for all that he has done for Genesis emulation scene.";}, + LBUF{txt="- Tasco Deluxe for his reverse engineering work on SVP and some mappers.";}, LBUF{txt="- Bart Trzynadlowski for his SSFII and 68000 docs.";}, - LBUF{txt="- Maze for his research (http://haze.mameworld.info).";}, + LBUF{txt="- Haze for his research (http://haze.mameworld.info).";}, + LBUF{txt="- Lordus, Exophase and Rokas for various ideas.";}, LBUF{txt="- Mark and Jean-loup for zlib library.";}, LBUF{txt="- Peter van Sebille for his various open-source Symbian projects to learn from.";}, LBUF{txt="- Steve Fischer for his open-source Motorola projects.";}, diff --git a/platform/uiq3/rsc/picodrive_loc.rss b/platform/uiq3/rsc/picodrive_loc.rss new file mode 100644 index 0000000..32b6475 --- /dev/null +++ b/platform/uiq3/rsc/picodrive_loc.rss @@ -0,0 +1,56 @@ +#include +#include + +#define EViewIdPrimaryView 1 + +// This file localise the applications icons and caption +RESOURCE LOCALISABLE_APP_INFO +{ + caption_and_icon = + { + CAPTION_AND_ICON_INFO + { + caption = "PicoDrive"; + // Icons are used to represent applications in the + // application launcher and application title bar. + // The number_of_icons value identifies how many icons + // that exist in the icon_file. + number_of_icons = 3; + // Using the application icons. + icon_file = "\\Resource\\Apps\\PicoDrive.mbm"; + } + }; + + view_list = + { + VIEW_DATA + { + uid=EViewIdPrimaryView; + screen_mode=0; + caption_and_icon = + { + CAPTION_AND_ICON_INFO + { + caption = "PicoDrive"; + number_of_icons = 3; + icon_file = "\\Resource\\Apps\\PicoDrive.mbm"; + } + }; + }, + + VIEW_DATA + { + uid=EViewIdPrimaryView; + screen_mode=EQikScreenModeSmallPortrait; + caption_and_icon = + { + CAPTION_AND_ICON_INFO + { + caption = "PicoDrive"; + number_of_icons = 3; + icon_file = "\\Resource\\Apps\\PicoDrive.mbm"; + } + }; + } + }; +} diff --git a/platform/uiq3/rsc/PicoDrive_reg.rss b/platform/uiq3/rsc/picodrive_reg.rss similarity index 83% rename from platform/uiq3/rsc/PicoDrive_reg.rss rename to platform/uiq3/rsc/picodrive_reg.rss index 2b2e4d8..d95f515 100644 --- a/platform/uiq3/rsc/PicoDrive_reg.rss +++ b/platform/uiq3/rsc/picodrive_reg.rss @@ -1,10 +1,10 @@ // All registration files need to #include appinfo.rh. -#include +#include // All registration files must define UID2, which is always // KUidAppRegistrationResourceFile, and UID3, which is the application's UID. -UID2 KUidAppRegistrationResourceFile -UID3 0xA00010F3 // application UID +// ? UID2 KUidAppRegistrationResourceFile +// ? UID3 0xA00010F3 // application UID // Registration file need to containo an APP_REGISTRATION_INFO resource that // minimally needs to provide the name of the application binary (using the diff --git a/platform/uiq3/uiq3.mak b/platform/uiq3/uiq3.mak new file mode 100644 index 0000000..6221f3f --- /dev/null +++ b/platform/uiq3/uiq3.mak @@ -0,0 +1,167 @@ +####################################################################### +### App specific part - this must be defined +#NAME="AnimatedStereogram" +#VENDOR="Pal Szasz" +#UID2=100039CE +#UID3=E0004201 +#EPOCLIBS="euser.lib apparc.lib cone.lib eikcore.lib eikcoctl.lib qikcore.lib fbscli.lib estlib.lib" +#EPOCROOT=/opt/space/uiq3/ +####################################################################### + +APPNAME ?= "UIQ3 Program" +VENDOR ?= "Somebody" +UID2 ?= 100039CE +UID3 ?= E0001001 +VER_MAJ ?= 1 +VER_MIN ?= 0 +STACK ?= 0x1000 +HEAP ?= 0x1000,0x100000 +EPOCROOT ?= /opt/space/uiq3/ +GCCPREF ?= arm-none-symbianelf +GCCPATH ?= $(EPOCROOT)/gcc +GCCVER ?= 3.4.3 + +export EPOCROOT + +NAME_ := $(shell echo $(APPNAME) | sed 's: ::g') +NAME := $(shell perl -e "print lc(\"$(NAME_)\")") +EPOCLIBS += euser.lib apparc.lib cone.lib eikcore.lib eikcoctl.lib qikcore.lib fbscli.lib estlib.lib qikallocdll.lib +EPOCREL = $(EPOCROOT)/epoc32/release/armv5 +CC = $(GCCPREF)-gcc +CXX = $(GCCPREF)-g++ +AS = $(GCCPREF)-as +LD = $(GCCPREF)-ld +ELF2E32 = elf2e32 +BMCONV = bmconv +EPOCRC = EPOCROOT=$(EPOCROOT) epocrc + +PATH := $(EPOCROOT)/bin:$(GCCPATH)/bin:$(GCCPATH)/$(GCCPREF)/bin:$(PATH) + +# TODO: do we really need -mapcs? +# -march=armv5t +CFLAGS += -Wall -pipe -nostdinc -msoft-float +CFLAGS += -DNDEBUG -D_UNICODE -D__GCCE__ -D__SYMBIAN32__ -D__EPOC32__ -D__MARM__ +CFLAGS += -D__EABI__ -D__MARM_ARMV5__ -D__EXE__ -D__SUPPORT_CPP_EXCEPTIONS__ -D__MARM_ARMV5__ +CFLAGS += -D__PRODUCT_INCLUDE__="$(EPOCROOT)/epoc32/include/variant/uiq_3.0.hrh" +CFLAGS += -include $(EPOCROOT)/epoc32/include/gcce/gcce.h +CFLAGS += -I$(EPOCROOT)/epoc32/include -I$(EPOCROOT)/epoc32/include/libc \ + -I$(EPOCROOT)/epoc32/include/variant -I$(GCCPATH)/lib/gcc/arm-none-symbianelf/$(GCCVER)/include/ +# can't optimize .cpp without -fno-unit-at-a-time +CXXFLAGS += $(CFLAGS) -c -x c++ -mapcs -Wno-ctor-dtor-privacy -Wno-unknown-pragmas -fexceptions -fno-unit-at-a-time + +LDFLAGS += -L$(GCCPATH)/lib -L$(GCCPATH)/lib/gcc/$(GCCPREF)/$(GCCVER) -L $(GCCPATH)/$(GCCPREF)/lib +LDFLAGS += --target1-abs --no-undefined -nostdlib -shared +LDFLAGS += -Ttext 0x8000 -Tdata 0x400000 --default-symver +LDFLAGS += -soname $(NAME){000a0000}\[$(UID3)\].exe --entry _E32Startup -u _E32Startup +LDFLAGS += $(EPOCROOT)/epoc32/release/armv5/urel/eexe.lib +LDFLAGS += -o $(NAME).elf.exe -Map $(NAME).exe.map +LDFLAGS2 = $(EPOCREL)/urel/qikalloc.lib $(EPOCREL)/lib/euser.dso +LDFLAGS2 += $(shell for i in $(EPOCLIBS); do echo -n " $(EPOCREL)/lib/$${i%%.lib}.dso "; done) +LDFLAGS2 += $(EPOCREL)/urel/usrt2_2.lib +LDFLAGS2 += $(shell for i in dfpaeabi dfprvct2_2 drtaeabi scppnwdl drtrvct2_2; do echo -n " $(EPOCREL)/lib/$$i.dso "; done) +LDFLAGS2 += -lsupc++ -lgcc + +E32FLAGS += --sid=0x$(UID3) --uid1=0x1000007a --uid2=0x$(UID2) --uid3=0x$(UID3) +E32FLAGS += --capability=none --fpu=softvfp --targettype=EXE +E32FLAGS += --output=$(NAME).exe --elfinput=$(NAME).elf.exe +E32FLAGS += --stack=$(STACK) +E32FLAGS += --heap=$(HEAP) +E32FLAGS += --linkas=$(NAME){000a0000}[$(UID3)].exe --libpath=$(EPOCREL)/lib + +EPOCRCFLAGS += -I../inc -I- -I$(EPOCROOT)/epoc32/include -I$(EPOCROOT)/epoc32/include/variant -DLANGUAGE_SC + +ICONS ?= $(shell echo ../data/appicon/*.bmp) +APPICON ?= $(NAME)appicon.mbm +RSCDIR ?= ../rsc +REGDIR ?= ../reg + +SRCH += $(shell echo ../inc/*.h) +SRC += $(shell echo ../src/*.cpp) +SRCRES ?= $(shell echo $(RSCDIR)/*.rss $(RSCDIR)/*.rls $(REGDIR)/*.rss $(REGDIR)/*.rls) +OBJ ?= $(SRC:.cpp=.o) + +.PHONY : all mbm icon_mbm rsc reg loc bin sis run + +#all : sis + +sis : $(NAME).sis + +icon_mbm : $(APPICON) + +mbm : + +$(NAME)appicon.mbg $(NAME)appicon.mbm : $(ICONS) + @echo "Creating multibitmap file..." + $(BMCONV) /h$(NAME)appicon.mbg $(NAME)appicon.mbm \ + /c24../data/appicon/icon_small.bmp\ + /8../data/appicon/icon_small_mask.bmp\ + /c24../data/appicon/icon_large.bmp\ + /8../data/appicon/icon_large_mask.bmp\ + /c24../data/appicon/icon_xlarge.bmp\ + /8../data/appicon/icon_xlarge_mask.bmp + +rsc : $(RSCDIR)/$(NAME).rsc + +$(RSCDIR)/$(NAME).rsc : $(RSCDIR)/$(NAME).rss # $(RSCDIR)/$(NAME).rls + @echo "Creating $@ ..." + $(EPOCRC) $(EPOCRCFLAGS) -I$(RSCDIR) -u $(RSCDIR)/$(NAME).rss \ + -o$(RSCDIR)/$(NAME).rsc -h$(RSCDIR)/$(NAME).rsg -t/tmp -l$(RSCDIR) + +reg : $(REGDIR)/$(NAME)_reg.rsc + +$(REGDIR)/$(NAME)_reg.rsc : $(REGDIR)/$(NAME)_reg.rss + @echo "Creating $@ ..." + RC_UID2=0x101f8021 RC_UID3=0x$(UID3) $(EPOCRC) $(EPOCRCFLAGS) -I$(REGDIR) \ + -u $(REGDIR)/$(NAME)_reg.rss -o$(REGDIR)/$(NAME)_reg.rsc -h$(REGDIR)/$(NAME)_reg.rsg -t/tmp -l$(REGDIR) + +loc : $(REGDIR)/$(NAME)_loc.rsc + +$(REGDIR)/$(NAME)_loc.rsc : $(REGDIR)/$(NAME)_loc.rss + @echo "Creating $@ ..." + $(EPOCRC) $(EPOCRCFLAGS) -I$(REGDIR) \ + -u $(REGDIR)/$(NAME)_loc.rss -o$(REGDIR)/$(NAME)_loc.rsc -h$(REGDIR)/$(NAME)_loc.rsg -t/tmp -l$(REGDIR) + +bin : bin_elf + @echo "Elf -> E32" + $(ELF2E32) $(E32FLAGS) + +bin_elf : $(NAME).elf.exe + +$(NAME).elf.exe : $(OBJ) $(EXTRALIB) + @echo "Linking..." + $(LD) $(LDFLAGS) $(OBJ) $(EXTRALIB) $(LDFLAGS2) + +.cpp.o : + @echo "Compiling $< ..." + $(CXX) $(CXXFLAGS) -o $@ $< + +$(NAME).sis : icon_mbm mbm rsc reg loc bin + rm -f $(NAME).sis + makesis $(NAME).pkg + mv $(NAME).SIS $(NAME).sis + +#cat $(EPOCROOT)/extra/in.pkg extra.pkg | \ +# sed "s:APPNAME:$(APPNAME):g" | \ +# sed "s:NAME:$(NAME):g" | \ +# sed "s:VER_MAJ:$(VER_MAJ):g" | \ +# sed "s:VER_MIN:$(VER_MIN):g" | \ +# sed "s:UID3:$(UID3):g" | \ +# sed "s:VENDOR:$(VENDOR):g" > $(NAME).pkg + +run : sis + xterm -e "to-phone m600 $(NAME).sis" + +-include .deps + +.deps : $(SRC) $(SRCH) + echo > $@ + $(CXX) -M -DDEPS $(SRC) $(CXXFLAGS) >> $@ + +clean : + rm -f $(NAME).exe $(NAME).elf.exe $(OBJ) tags .deps $(NAME).exe.map + rm -f *.bkp ../src/*.bkp ../inc/*.bkp $(RSCDIR)/*.bkp $(REGDIR)/*.bkp + rm -f $(RSCDIR)/*.rsc $(RSCDIR)/*.rsg + rm -f $(REGDIR)/*.rsc $(REGDIR)/*.rsg + rm -f $(APPICON) $(NAME)appicon.mbg $(NAME).mbg + + diff --git a/platform/uiq3/version.h b/platform/uiq3/version.h index 007068a..4388a01 100644 --- a/platform/uiq3/version.h +++ b/platform/uiq3/version.h @@ -3,8 +3,8 @@ #ifndef __VERSION_H #define __VERSION_H -#define KPicoMajorVersionNumber 0 -#define KPicoMinorVersionNumber 96 +#define KPicoMajorVersionNumber 1 +#define KPicoMinorVersionNumber 51 #define KPicoBuildNumber 0 #endif /* __VERSION_H */ diff --git a/unzip/unzip.c b/unzip/unzip.c index c055969..ddf59aa 100644 --- a/unzip/unzip.c +++ b/unzip/unzip.c @@ -5,11 +5,7 @@ #include #include -#ifdef __SYMBIAN32__ -#include -#else #include "zlib/zlib.h" -#endif /* public globals */ //int gUnzipQuiet = 0; /* flag controls error messages */ diff --git a/unzip/unzip_stream.c b/unzip/unzip_stream.c index 3e7919b..7f3e935 100644 --- a/unzip/unzip_stream.c +++ b/unzip/unzip_stream.c @@ -6,11 +6,7 @@ #include #include -#ifdef __SYMBIAN32__ -#include -#else #include "zlib/zlib.h" -#endif #define errormsg(str1,def,fname) printf("%s: " #def ": " str1 "\n", fname); diff --git a/zlib/zconf.h b/zlib/zconf.h index 03a9431..6f799fd 100644 --- a/zlib/zconf.h +++ b/zlib/zconf.h @@ -305,6 +305,10 @@ typedef uLong FAR uLongf; # define NO_vsnprintf #endif +#if defined(__EPOC32__) +# define NO_vsnprintf +#endif + #if defined(__MVS__) # define NO_vsnprintf # ifdef FAR -- 2.39.2