From e1a4cb6c5b089bbd94cf4c7ca74896d4f5df3585 Mon Sep 17 00:00:00 2001 From: WizzardSK Date: Mon, 27 Jul 2026 19:05:30 +0200 Subject: [PATCH] Sync with upstream BlastEm (hg tip 732f5689d438) This repo has been sitting on the July 2022 state of upstream BlastEm. The sync brings in four years of upstream work, most importantly for this repo the DSL generated CPU cores: upstream's Makefile now switches to NEW_CORE automatically on any architecture that has no native code generator, and genesis.h picks up the generated m68k context in that configuration, which is what makes non-x86 builds possible at all. Only .gitlab-ci.yml and Makefile.libretro are kept from this repo, every other file is upstream's. --- .hgignore | 15 +- 32x.c | 1508 +++ 32x.h | 139 + 32x_video.c | 849 ++ 32x_video.h | 75 + 68kinst.c | 762 +- 68kinst.h | 8 +- Android.mk | 31 +- Makefile | 338 +- README | 216 +- android/AndroidManifest.xml | 55 - android/ant.properties | 17 - android/app/build.gradle | 75 + android/{ => app}/jni/Android.mk | 1 - android/app/jni/Application.mk | 10 + android/app/jni/src | 1 + android/app/proguard-rules.pro | 98 + android/app/src/main/AndroidManifest.xml | 101 + android/app/src/main/assets/default.cfg | 1 + android/app/src/main/assets/images | 1 + android/app/src/main/assets/rom.db | 1 + android/app/src/main/assets/shaders | 1 + android/app/src/main/assets/systems.cfg | 1 + .../com/retrodev/blastem/BlastEmActivity.java | 158 + android/app/src/main/java/org | 1 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 834 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 755 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 897 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1026 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1201 bytes android/app/src/main/res/values/colors.xml | 6 + .../{ => app/src/main}/res/values/strings.xml | 1 - android/app/src/main/res/values/styles.xml | 7 + android/assets/default.cfg | 395 - android/assets/images | 1 - android/assets/menu.bin | 1 - android/assets/rom.db | 1 - android/assets/shaders | 1 - android/build.gradle | 25 + android/build.properties | 17 - android/build.xml | 93 - android/default.properties | 11 - android/gradle.properties | 17 + android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54213 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + android/gradlew | 160 + android/gradlew.bat | 90 + android/jni/Application.mk | 8 - android/jni/src | 1 - android/proguard-project.txt | 20 - android/project.properties | 14 - android/res/drawable-hdpi/ic_launcher.png | Bin 306 -> 0 bytes android/res/drawable-mdpi/ic_launcher.png | Bin 240 -> 0 bytes android/res/drawable-xhdpi/ic_launcher.png | Bin 368 -> 0 bytes android/res/drawable-xxhdpi/ic_launcher.png | Bin 484 -> 0 bytes android/res/layout/main.xml | 13 - android/settings.gradle | 1 + .../com/retrodev/blastem/BlastEmActivity.java | 22 - android/src/org/libsdl/app/SDL.java | 37 - android/src/org/libsdl/app/SDLActivity.java | 1595 --- .../src/org/libsdl/app/SDLAudioManager.java | 178 - .../org/libsdl/app/SDLControllerManager.java | 433 - backend.c | 770 +- backend.h | 30 +- backend_x86.c | 695 +- bindings.c | 241 +- bindings.h | 8 +- blastcpm.c | 27 +- blastem.c | 488 +- blastem.exe.manifest | 15 + blastem.h | 2 +- blastem.rc | 9 +- build_nightly | 4 +- build_release | 46 +- build_upload_web_nightly | 76 + build_upload_win_nightly | 2 +- cd_graphics.c | 354 + cd_graphics.h | 27 + cdd_fader.c | 99 + cdd_fader.h | 26 + cdd_mcu.c | 901 ++ cdd_mcu.h | 179 + cdimage.c | 732 ++ cdimage.h | 12 + chd.c | 240 + chd.h | 94 + coleco.c | 572 + coleco.h | 35 + comparetests.py | 89 +- config.c | 461 +- config.h | 14 + controller_info.c | 136 +- controller_info.h | 6 +- cpu_dsl.py | 1894 +++- debug.c | 7615 +++++++++++-- debug.h | 218 +- default.cfg | 77 +- dis.c | 534 +- disasm.c | 593 + disasm.h | 53 + emscripten_pre.js | 10 + event_log.c | 228 +- event_log.h | 15 +- flac.c | 657 ++ flac.h | 59 + gamecontrollerdb.txt | 2284 +++- gdb_remote.c | 152 +- gdb_remote.h | 2 +- gen_player.c | 19 +- gen_x86.c | 384 +- gen_x86.h | 57 + genesis.c | 2461 +++- genesis.h | 30 +- gentests.py | 126 +- gst.c | 133 +- hash.c | 2 +- i2c.c | 114 + i2c.h | 8 + i8255.c | 290 + i8255.h | 28 + img2tiles.py | 72 +- io.c | 425 +- io.h | 17 +- jagcpu.h | 2 + jcart.c | 14 +- korean_sms_multi.c | 82 + korean_sms_multi.h | 9 + laseractive.c | 552 + laseractive.h | 23 + lc8951.c | 496 + lc8951.h | 52 + libblastem.c | 667 +- libretro.h | 7645 ++++++++++--- m68k.cpu | 2652 ++++- m68k_core.c | 303 +- m68k_core.h | 58 +- m68k_core_x86.c | 409 +- m68k_internal.h | 5 +- m68k_util.c | 320 +- maketests.py | 6 +- mediaplayer.c | 1135 ++ mediaplayer.h | 87 + megawifi.c | 2 +- mem.c | 10 +- memmap.h | 4 + menu.c | 14 +- multi_game.c | 10 +- net.c | 39 +- net.h | 10 + net_win.c | 43 + nor.c | 16 +- notes/cdd_mcu_notes.txt | 429 + notes.txt => notes/notes.txt | 0 .../sf2_50mhz_analysis.txt | 0 sf2_vram_map.txt => notes/sf2_vram_map.txt | 0 nuklear_ui/blastem_nuklear.c | 1114 +- nuklear_ui/blastem_nuklear.h | 5 + nuklear_ui/debug_ui.c | 250 + nuklear_ui/debug_ui.h | 8 + nuklear_ui/filechooser.h | 7 + nuklear_ui/filechooser_gtk.c | 179 + nuklear_ui/filechooser_null.c | 12 + nuklear_ui/filechooser_win.c | 39 + nuklear_ui/font.c | 94 +- nuklear_ui/font_android.c | 3 +- nuklear_ui/font_web.c | 46 + nuklear_ui/nuklear.h | 9 +- nuklear_ui/nuklear_sdl_gles2.h | 140 +- nuklear_ui/sfnt.c | 12 + nuklear_ui/sfnt.h | 1 + oscilloscope.c | 133 + oscilloscope.h | 27 + osd_font.c | 96 + paths.c | 514 +- paths.h | 34 +- pd0178.c | 103 + pd0178.h | 26 + pico_pcm.c | 313 + pico_pcm.h | 39 + pixel.h | 30 + png.c | 122 +- png.h | 17 +- ppm.c | 19 +- ppm.h | 3 +- psg.c | 70 +- psg.h | 5 + radica.c | 114 + radica.h | 14 + realtec.c | 2 +- render.h | 37 +- render_audio.c | 99 +- render_audio.h | 5 +- render_sdl.c | 1327 ++- render_sdl.h | 9 +- rf5c164.c | 324 + rf5c164.h | 46 + rom.db | 668 +- romdb.c | 1156 +- romdb.h | 20 +- sega_mapper.c | 368 +- sega_mapper.h | 28 + segacd.c | 1907 ++++ segacd.h | 75 + serialize.c | 13 +- serialize.h | 18 +- sft_mapper.c | 27 + sft_mapper.h | 7 + sh2.cpu | 1432 +++ sh2_decode.c | 449 + sh2_decode.h | 163 + sh2_util.c | 859 ++ sh2dis.c | 409 + sh7095.c | 987 ++ sh7095.h | 153 + shaders/bandlimit_pixel.f.glsl | 149 + shaders/extra_window.f.glsl | 9 + shaders/extra_window.v.glsl | 11 + shaders/ntsc.f.glsl | 210 + shaders/xBRZ.f.glsl | 297 + sms.c | 1175 +- sms.h | 17 +- system.c | 467 +- system.h | 178 +- system_header.h | 6 - systems.cfg | 49 +- terminal.c | 12 +- tern.c | 29 +- tern.h | 1 + test.c | 76 - test_arm.c | 29 - test_int_timing.c | 101 - test_x86.c | 48 - testcases.txt | 12 +- testgst.c | 81 - tmss.s68 | 43 +- trans.c | 26 +- upd78k2.cpu | 1850 +++ upd78k2_dis.c | 889 ++ upd78k2_dis.h | 28 + upd78k2_util.c | 777 ++ upd78k2run.c | 65 + upddis.c | 353 + util.c | 996 +- util.h | 74 +- vdp.c | 3480 ++++-- vdp.h | 88 +- version.inc | 1 + vgm.c | 55 +- vgm.h | 70 +- vgmplay.c | 255 - wave.c | 41 +- wave.h | 4 +- xband.c | 2 +- ym2612.c | 397 +- ym2612.h | 121 +- ym_common.c | 226 + ym_common.h | 72 + ymf262.c | 337 + ymf262.h | 51 + ymz263b.c | 602 + ymz263b.h | 58 + z80.cpu | 766 +- z80_to_x86.c | 763 +- z80_to_x86.h | 22 + z80_util.c | 4 + z80inst.c | 4 +- z80inst.h | 4 +- zcompare.py | 57 +- zdis.c | 33 +- zip.c | 9 +- zip.h | 2 +- zlib/LICENSE | 22 + zlib/adler32.c | 32 +- zlib/compress.c | 21 +- zlib/crc32.c | 1283 ++- zlib/crc32.h | 9877 ++++++++++++++++- zlib/deflate.c | 880 +- zlib/deflate.h | 74 +- zlib/gzclose.c | 4 +- zlib/gzguts.h | 34 +- zlib/gzlib.c | 119 +- zlib/gzread.c | 108 +- zlib/gzwrite.c | 118 +- zlib/infback.c | 50 +- zlib/inffast.c | 33 +- zlib/inffast.h | 2 +- zlib/inflate.c | 185 +- zlib/inflate.h | 5 +- zlib/inftrees.c | 17 +- zlib/inftrees.h | 12 +- zlib/trees.c | 674 +- zlib/uncompr.c | 16 +- zlib/zconf.h | 37 +- zlib/zlib.h | 594 +- zlib/zutil.c | 66 +- zlib/zutil.h | 65 +- ztestgen.c | 1 - ztestrun.c | 15 +- 298 files changed, 76400 insertions(+), 15370 deletions(-) create mode 100644 32x.c create mode 100644 32x.h create mode 100644 32x_video.c create mode 100644 32x_video.h delete mode 100644 android/AndroidManifest.xml delete mode 100644 android/ant.properties create mode 100644 android/app/build.gradle rename android/{ => app}/jni/Android.mk (75%) create mode 100644 android/app/jni/Application.mk create mode 120000 android/app/jni/src create mode 100644 android/app/proguard-rules.pro create mode 100644 android/app/src/main/AndroidManifest.xml create mode 120000 android/app/src/main/assets/default.cfg create mode 120000 android/app/src/main/assets/images create mode 120000 android/app/src/main/assets/rom.db create mode 120000 android/app/src/main/assets/shaders create mode 120000 android/app/src/main/assets/systems.cfg create mode 100644 android/app/src/main/java/com/retrodev/blastem/BlastEmActivity.java create mode 120000 android/app/src/main/java/org create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/values/colors.xml rename android/{ => app/src/main}/res/values/strings.xml (64%) create mode 100644 android/app/src/main/res/values/styles.xml delete mode 100644 android/assets/default.cfg delete mode 120000 android/assets/images delete mode 120000 android/assets/menu.bin delete mode 120000 android/assets/rom.db delete mode 120000 android/assets/shaders create mode 100644 android/build.gradle delete mode 100644 android/build.properties delete mode 100644 android/build.xml delete mode 100644 android/default.properties create mode 100644 android/gradle.properties create mode 100644 android/gradle/wrapper/gradle-wrapper.jar create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100755 android/gradlew create mode 100644 android/gradlew.bat delete mode 100644 android/jni/Application.mk delete mode 120000 android/jni/src delete mode 100644 android/proguard-project.txt delete mode 100644 android/project.properties delete mode 100644 android/res/drawable-hdpi/ic_launcher.png delete mode 100644 android/res/drawable-mdpi/ic_launcher.png delete mode 100644 android/res/drawable-xhdpi/ic_launcher.png delete mode 100644 android/res/drawable-xxhdpi/ic_launcher.png delete mode 100644 android/res/layout/main.xml create mode 100644 android/settings.gradle delete mode 100644 android/src/com/retrodev/blastem/BlastEmActivity.java delete mode 100644 android/src/org/libsdl/app/SDL.java delete mode 100644 android/src/org/libsdl/app/SDLActivity.java delete mode 100644 android/src/org/libsdl/app/SDLAudioManager.java delete mode 100644 android/src/org/libsdl/app/SDLControllerManager.java create mode 100644 blastem.exe.manifest create mode 100755 build_upload_web_nightly create mode 100644 cd_graphics.c create mode 100644 cd_graphics.h create mode 100644 cdd_fader.c create mode 100644 cdd_fader.h create mode 100644 cdd_mcu.c create mode 100644 cdd_mcu.h create mode 100644 cdimage.c create mode 100644 cdimage.h create mode 100644 chd.c create mode 100644 chd.h create mode 100644 coleco.c create mode 100644 coleco.h create mode 100644 disasm.c create mode 100644 disasm.h create mode 100644 emscripten_pre.js create mode 100644 flac.c create mode 100644 flac.h create mode 100644 i8255.c create mode 100644 i8255.h create mode 100644 korean_sms_multi.c create mode 100644 korean_sms_multi.h create mode 100644 laseractive.c create mode 100644 laseractive.h create mode 100644 lc8951.c create mode 100644 lc8951.h create mode 100644 mediaplayer.c create mode 100644 mediaplayer.h create mode 100644 notes/cdd_mcu_notes.txt rename notes.txt => notes/notes.txt (100%) rename sf2_50mhz_analysis.txt => notes/sf2_50mhz_analysis.txt (100%) rename sf2_vram_map.txt => notes/sf2_vram_map.txt (100%) create mode 100644 nuklear_ui/debug_ui.c create mode 100644 nuklear_ui/debug_ui.h create mode 100644 nuklear_ui/filechooser.h create mode 100644 nuklear_ui/filechooser_gtk.c create mode 100644 nuklear_ui/filechooser_null.c create mode 100644 nuklear_ui/filechooser_win.c create mode 100644 nuklear_ui/font_web.c create mode 100644 oscilloscope.c create mode 100644 oscilloscope.h create mode 100644 osd_font.c create mode 100644 pd0178.c create mode 100644 pd0178.h create mode 100644 pico_pcm.c create mode 100644 pico_pcm.h create mode 100644 pixel.h create mode 100644 radica.c create mode 100644 radica.h create mode 100644 rf5c164.c create mode 100644 rf5c164.h create mode 100644 segacd.c create mode 100644 segacd.h create mode 100644 sft_mapper.c create mode 100644 sft_mapper.h create mode 100644 sh2.cpu create mode 100644 sh2_decode.c create mode 100644 sh2_decode.h create mode 100644 sh2_util.c create mode 100644 sh2dis.c create mode 100644 sh7095.c create mode 100644 sh7095.h create mode 100644 shaders/bandlimit_pixel.f.glsl create mode 100644 shaders/extra_window.f.glsl create mode 100644 shaders/extra_window.v.glsl create mode 100644 shaders/ntsc.f.glsl create mode 100644 shaders/xBRZ.f.glsl delete mode 100644 system_header.h delete mode 100644 test.c delete mode 100644 test_arm.c delete mode 100644 test_int_timing.c delete mode 100644 test_x86.c delete mode 100644 testgst.c create mode 100644 upd78k2.cpu create mode 100644 upd78k2_dis.c create mode 100644 upd78k2_dis.h create mode 100644 upd78k2_util.c create mode 100644 upd78k2run.c create mode 100644 upddis.c create mode 100644 version.inc delete mode 100644 vgmplay.c create mode 100644 ym_common.c create mode 100644 ym_common.h create mode 100644 ymf262.c create mode 100644 ymf262.h create mode 100644 ymz263b.c create mode 100644 ymz263b.h create mode 100644 zlib/LICENSE diff --git a/.hgignore b/.hgignore index 00ceaec..88b40aa 100644 --- a/.hgignore +++ b/.hgignore @@ -21,12 +21,17 @@ generated_tests/* ztests glew/* lib/* -android/obj -android/bin -android/gen -android/libs +obj/* +z80.c +z80.h +m68k.c +m68k.h +sh2.c +sh2.h +android/.gradle +android/app/build +android/app/jni/SDL android/local.properties -android/jni/SDL sdl *.o *.list diff --git a/32x.c b/32x.c new file mode 100644 index 0000000..1b313a3 --- /dev/null +++ b/32x.c @@ -0,0 +1,1508 @@ +#include +#include +#include +#include "32x.h" +#include "sh7095.h" +#include "genesis.h" +#include "sega_mapper.h" +#include "blastem.h" +#include "util.h" +#include "debug.h" + +#define MAX_SH2_CYCLES 2000 + +#ifdef DO_DEBUG_PRINT +#define dprintf printf +#else +#define dprintf +#endif + +void pwm_fifo_write(pwm_fifo *fifo, uint16_t *status, uint16_t value) +{ + fifo->fifo[fifo->write++] = value & 0xFFF; + fifo->write %= 3; + if (*status & BIT_PWM_FULL) { + //FIFO was already full, oldest value was overwritten so advance read + fifo->read++; + fifo->read %= 3; + } else if (fifo->read == fifo->write) { + *status |= BIT_PWM_FULL; + } + *status &= ~BIT_PWM_EMPTY; +} + +void pwm_fifo_read(pwm_fifo *fifo, uint16_t *status, uint16_t cycle, int16_t *out) +{ + if (!(*status & BIT_PWM_EMPTY)) { + uint16_t sample = fifo->fifo[fifo->read++]; + if (sample > cycle) { + sample = cycle; + } + *out = sample * 0x200 / cycle - 0x100; + fifo->read %= 3; + if (fifo->read == fifo->write) { + *status |= BIT_PWM_EMPTY; + } else { + *status &= ~BIT_PWM_FULL; + } + } +} + +#define PWM_DECIMATE 64 + +static uint32_t pwm_tick(s32x *mars, uint32_t ticks) +{ + if (ticks >= mars->pwm_decimate) { + ticks = mars->pwm_decimate; + mars->pwm_counter -= mars->pwm_decimate; + mars->pwm_left_accum += mars->pwm_left * mars->pwm_decimate; + mars->pwm_right_accum += mars->pwm_right * mars->pwm_decimate; + render_put_stereo_sample(mars->pwm, mars->pwm_left_accum, mars->pwm_right_accum); + if (mars->scope) { + scope_add_sample(mars->scope, mars->scope_left, mars->pwm_left_accum, 0); + scope_add_sample(mars->scope, mars->scope_right, mars->pwm_right_accum, 0); + } + mars->pwm_left_accum = mars->pwm_right_accum = 0; + mars->pwm_decimate = PWM_DECIMATE; + } else { + mars->pwm_left_accum += mars->pwm_left * ticks; + mars->pwm_right_accum += mars->pwm_right * ticks; + mars->pwm_decimate -= ticks; + mars->pwm_counter -= ticks; + } + return ticks; +} + +static void s32x_pwm_run(s32x *mars, uint32_t target) +{ + if (target <= mars->pwm_cycle) { + return; + } + uint32_t ticks = (target - mars->pwm_cycle) / 7; + mars->pwm_cycle += 7 * ticks; + if (mars->pwm_cycle < target) { + ticks++; + mars->pwm_cycle += 7; + } + if (mars->regs[S32X_PWM_CTRL] & S32X_PWM_LRMD) { + while (ticks) + { + if (mars->pwm_counter == 2) { + uint16_t cycle = mars->pwm_counter = mars->regs[S32X_PWM_CYCLE]; + if (!cycle) { + cycle = 0x1000; + } + switch (mars->regs[S32X_PWM_CTRL] & 3) + { + case 1: + pwm_fifo_read(&mars->fifo_left, mars->regs + S32X_PWM_WIDTH_L, cycle, &mars->pwm_left); + break; + case 2: + pwm_fifo_read(&mars->fifo_right, mars->regs + S32X_PWM_WIDTH_R, cycle, &mars->pwm_left); + break; + //TODO: what happens if the illegal 3 value is used + } + switch (mars->regs[S32X_PWM_CTRL] >> 2 & 3) + { + case 1: + pwm_fifo_read(&mars->fifo_right, mars->regs + S32X_PWM_WIDTH_R, cycle, &mars->pwm_right); + break; + case 2: + pwm_fifo_read(&mars->fifo_left, mars->regs + S32X_PWM_WIDTH_L, cycle, &mars->pwm_right); + break; + } + mars->pwm_timer--; + mars->pwm_timer &= 0xF; + //TODO: test where the PWM int mask is applied + if (!mars->pwm_timer) { + mars->pwm_main_int_pending = mars->pwm_sub_int_pending = 1; + mars->pwm_timer = mars->regs[S32X_PWM_CTRL] >> 8 & 0xF; + } + if (mars->regs[S32X_PWM_CTRL] & BIT_PWM_RTP) { + sh7095_assert_dreq1(mars->main); + sh7095_assert_dreq1(mars->sub); + } + ticks -= pwm_tick(mars, 1); + } else if (mars->pwm_counter != 1) { + if (mars->pwm_counter > 2 + mars->pwm_decimate || !mars->pwm_counter) { + ticks -= pwm_tick(mars, ticks); + } else { + ticks -= pwm_tick(mars, 1); + } + mars->pwm_counter &= 0xFFF; + } else { + mars->pwm_counter = mars->regs[S32X_PWM_CYCLE]; + if (mars->pwm_counter == 1) { + ticks -= pwm_tick(mars, ticks); + mars->pwm_counter = 1; + } else { + ticks -= pwm_tick(mars, 1); + mars->pwm_counter &= 0xFFF; + } + } + } + } else { + while (ticks) + { + ticks -= pwm_tick(mars, ticks); + } + } +} + +static void save_sh2_state(s32x *mars, sh2_context *sh2) +{ + sh2_context *dst = sh2->main ? mars->main_tmp : mars->sub_tmp; + memcpy(dst->gpr, sh2->gpr, sizeof(sh2->gpr)); + dst->vbr = sh2->vbr; + dst->sr = sh2->sr; + dst->pr = sh2->pr; + dst->pc = sh2->pc; + dst->macl = sh2->macl; + dst->mach = sh2->mach; + dst->gbr = sh2->gbr; + dst->prefetch_next = sh2->prefetch_next; + dst->prefetch_cur = sh2->prefetch_cur; + dst->t = sh2->t; + dst->s = sh2->s; + dst->q = sh2->q; + dst->m = sh2->m; + dst->delay_slot = sh2->delay_slot; + mars->saved_sh2_state = 1; +} + +static void maybe_restore_sh2(s32x *mars, sh2_context *sh2) +{ + if (mars->saved_sh2_state) { + mars->saved_sh2_state = 0; + sh2_context *src = sh2->main ? mars->main_tmp : mars->sub_tmp; + memcpy(sh2->gpr, src->gpr, sizeof(sh2->gpr)); + sh2->vbr = src->vbr; + sh2->sr = src->sr; + sh2->pr = src->pr; + sh2->pc = src->pc; + sh2->macl = src->macl; + sh2->mach = src->mach; + sh2->gbr = src->gbr; + sh2->prefetch_next = src->prefetch_next; + sh2->prefetch_cur = src->prefetch_cur; + sh2->t = src->t; + sh2->s = src->s; + sh2->q = src->q; + sh2->m = src->m; + sh2->delay_slot = src->delay_slot; + } +} + +void s32x_run(s32x *mars, uint32_t target) +{ + uint32_t sh2_target = target * 3; + if (sh2_target > mars->main->cycles) { + while (sh2_target > mars->main->cycles) + { + uint32_t cur_target; + if (sh2_target - mars->main->cycles > MAX_SH2_CYCLES) { + cur_target = mars->main->cycles + MAX_SH2_CYCLES; + } else { + cur_target = sh2_target; + } + mars->cur_sh2_target = cur_target; +#ifndef IS_LIB + if (mars->main_enter_debugger && !mars->main->reset) { + mars->main_enter_debugger = 0; + if (mars->main->need_reset) { + sh2_reset(mars->main); + } + sh2_debugger(mars->main); + } +#endif + sh2_run(mars->main, cur_target); + maybe_restore_sh2(mars, mars->main); +#ifndef IS_LIB + if (mars->sub_enter_debugger && !mars->sub->reset) { + mars->sub_enter_debugger = 0; + if (mars->sub->need_reset) { + sh2_reset(mars->sub); + } + sh2_debugger(mars->sub); + } +#endif + sh2_run(mars->sub, cur_target); + maybe_restore_sh2(mars, mars->sub); + s32x_pwm_run(mars, cur_target); + } + } + s32x_video_run(&mars->video, target); +} + +void main_sh2_next_int(sh2_context *sh2) +{ + s32x *mars = sh2->system; + uint32_t priority_mask = sh2->sr >> 4 & 0xF; + sh2->int_cycle = 0xFFFFFFFF; + sh2->int_priority = priority_mask; + sh2->int_ack = NULL; + if (priority_mask < 12) { + uint32_t vint_cycle = 0xFFFFFFFF; + if (mars->sh2_regs[S32X_SH2_INT_CTRL] & BIT_VERT_INT_EN) { + if (mars->video.main_vint_pending) { + vint_cycle = sh2->cycles; + } else { + vint_cycle = (mars->video.cycle + s32x_cycles_to_vblank(&mars->video)) * 3; + } + } + if (vint_cycle < sh2->int_cycle) { + sh2->int_cycle = vint_cycle; + sh2->int_vector = 70; + sh2->int_priority = 12; + } + if (priority_mask < 10) { + uint32_t hint_cycle = 0xFFFFFFFF; + if (mars->sh2_regs[S32X_SH2_INT_CTRL] & BIT_HORZ_INT_EN) { + if (mars->video.main_hint_pending) { + hint_cycle = sh2->cycles; + } else { + hint_cycle = (mars->video.cycle + s32x_cycles_to_hint(&mars->video)) * 3; + } + } + if (hint_cycle < sh2->int_cycle) { + sh2->int_cycle = hint_cycle; + sh2->int_vector = 69; + sh2->int_priority = 10; + } + if (priority_mask < 8) { + uint32_t cmd_int_cycle = 0xFFFFFFFF; + if ((mars->sh2_regs[S32X_SH2_INT_CTRL] & BIT_CMD_INT_EN) && (mars->regs[S32X_INT_CTRL] & BIT_MAIN_INT) ) { + cmd_int_cycle = sh2->cycles; + } + if (cmd_int_cycle < sh2->int_cycle) { + sh2->int_cycle = cmd_int_cycle; + sh2->int_vector = 68; + sh2->int_priority = 8; + } + if (priority_mask < 6) { + uint32_t pwm_int_cycle = 0xFFFFFFFF; + if (mars->sh2_regs[S32X_SH2_INT_CTRL] & BIT_PWM_INT_EN) { + sh2_run(mars->sub, sh2->cycles); + s32x_pwm_run(mars, sh2->cycles); + if (mars->pwm_main_int_pending) { + pwm_int_cycle = sh2->cycles; + } else if (mars->pwm_counter != 1) { + pwm_int_cycle = mars->pwm_cycle + 7 * ((mars->pwm_counter - 2) & 0xFFFF); + pwm_int_cycle += ((mars->pwm_timer - 1) & 0xF) * ((mars->regs[S32X_PWM_CYCLE] - 2) & 0xFFFF) * 7; + } + } + if (pwm_int_cycle < sh2->int_cycle) { + sh2->int_cycle = pwm_int_cycle; + sh2->int_vector = 67; + sh2->int_priority = 6; + } + } + } + } + } + sh7095_next_int(sh2, priority_mask); +} + +void sub_sh2_next_int(sh2_context *sh2) +{ + s32x *mars = sh2->system; + uint32_t priority_mask = sh2->sr >> 4 & 0xF; + sh2->int_cycle = 0xFFFFFFFF; + sh2->int_priority = priority_mask; + sh2->int_ack = NULL; + if (priority_mask < 12) { + uint32_t vint_cycle = 0xFFFFFFFF; + if (mars->sh2_regs[S32X_SH2_SUB_INT] & BIT_VERT_INT_EN) { + if (mars->video.sub_vint_pending) { + vint_cycle = sh2->cycles; + } else { + vint_cycle = (mars->video.cycle + s32x_cycles_to_vblank(&mars->video)) * 3; + } + } + if (vint_cycle < sh2->int_cycle) { + sh2->int_cycle = vint_cycle; + sh2->int_vector = 70; + sh2->int_priority = 12; + } + if (priority_mask < 10) { + uint32_t hint_cycle = 0xFFFFFFFF; + if (mars->sh2_regs[S32X_SH2_SUB_INT] & BIT_HORZ_INT_EN) { + if (mars->video.sub_hint_pending) { + hint_cycle = sh2->cycles; + } else { + hint_cycle = (mars->video.cycle + s32x_cycles_to_hint(&mars->video)) * 3; + } + } + if (hint_cycle < sh2->int_cycle) { + sh2->int_cycle = hint_cycle; + sh2->int_vector = 69; + sh2->int_priority = 10; + } + if (priority_mask < 8) { + uint32_t cmd_int_cycle = 0xFFFFFFFF; + if ((mars->sh2_regs[S32X_SH2_SUB_INT] & BIT_CMD_INT_EN) && mars->regs[S32X_INT_CTRL] & BIT_SUB_INT) { + cmd_int_cycle = sh2->cycles; + } + if (cmd_int_cycle < sh2->int_cycle) { + sh2->int_cycle = cmd_int_cycle; + sh2->int_vector = 68; + sh2->int_priority = 8; + } + if (priority_mask < 6) { + uint32_t pwm_int_cycle = 0xFFFFFFFF; + if (mars->sh2_regs[S32X_SH2_SUB_INT] & BIT_PWM_INT_EN) { + s32x_pwm_run(mars, sh2->cycles); + if (mars->pwm_sub_int_pending) { + pwm_int_cycle = sh2->cycles; + } else if (mars->pwm_counter != 1) { + pwm_int_cycle = mars->pwm_cycle + 7 * ((mars->pwm_counter - 2) & 0xFFFF); + pwm_int_cycle += ((mars->pwm_timer - 1) & 0xF) * ((mars->regs[S32X_PWM_CYCLE] - 2) & 0xFFFF) * 7; + } + } + if (pwm_int_cycle < sh2->int_cycle) { + sh2->int_cycle =pwm_int_cycle; + sh2->int_vector = 67; + sh2->int_priority = 6; + } + } + } + } + } + sh7095_next_int(sh2, priority_mask); +} + +void s32x_adjust_cycles(s32x *mars, uint32_t deduction) +{ + if (mars->video.cycle > deduction) { + mars->video.cycle -= deduction; + } else { + mars->video.cycle = 0; + } + deduction *= 3; + if (mars->main->cycles > deduction) { + mars->main->cycles -= deduction; + } else { + mars->main->cycles = 0; + } + sh7095_adjust_cycles(mars->main, deduction); + if (mars->sub->cycles > deduction) { + mars->sub->cycles -= deduction; + } else { + mars->sub->cycles = 0; + } + sh7095_adjust_cycles(mars->sub, deduction); + if (mars->pwm_cycle > deduction) { + mars->pwm_cycle -= deduction; + } else { + mars->pwm_cycle = 0; + } + main_sh2_next_int(mars->main); + sub_sh2_next_int(mars->sub); +} + +void s32x_enable_scope(s32x *mars, oscilloscope *scope, uint32_t main_clock) +{ + mars->scope = scope; + mars->scope_left = scope_add_channel(scope, "PWM Left", main_clock * 3 / (7 * PWM_DECIMATE)); + mars->scope_right = scope_add_channel(scope, "PWM Right", main_clock * 3 / (7 * PWM_DECIMATE)); +} + +void s32x_set_speed(s32x *mars, uint32_t main_clock) +{ + render_audio_adjust_clock(mars->pwm, main_clock * 3, 7 * PWM_DECIMATE); +} + +uint16_t s32x_68k_read(uint32_t address, void *vcontext) +{ + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + s32x *mars = gen->mars; + s32x_run(mars, m68k->cycles); + if (address < 0xA15100 + (S32X_NUM_REGS * 2)) { + uint32_t reg = (address & 0xFE) >> 1; + if (reg == S32X_PWM_WIDTH_M) { + //TODO: test what happens when reading the FIFO status bits here when L & R don't match + return mars->regs[S32X_PWM_WIDTH_L] & mars->regs[S32X_PWM_WIDTH_R]; + } else if (reg == S32X_DREQ_LEN) { + //supposedly the low two bits are always 0 here + //but it's convenient to use them to track trasnfer progress, so we just mask them here + return mars->regs[S32X_DREQ_LEN] & 0xFFFC; + } + return mars->regs[reg]; + } else if (address >= 0xA15180) { + while (mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM) { + gen->bus_busy = 1; + //FIXME: make this continue exactly when FM Is flipped + m68k->cycles += MAX_SH2_CYCLES / 3; +#ifdef NEW_CORE + m68k->sync_components(m68k, 0); +#else + m68k->opts->sync_components(m68k, 0); +#endif + } + gen->bus_busy = 0; + return s32x_video_68k_read(address, &mars->video); + } + return 0xFFFF; +} + +uint8_t s32x_68k_read_b(uint32_t address, void *vcontext) +{ + uint16_t val = s32x_68k_read(address & ~1, vcontext); + if (address & 1) { + return val; + } + return val >> 8; +} + +uint16_t s32x_sh2_read(uint32_t address, void *vcontext) +{ + sh2_context *sh2 = vcontext; + s32x *mars = sh2->system; + if (sh2->main) { + sh2_run(mars->sub, sh2->cycles); + } + if (address < 0x0004000 + (S32X_NUM_REGS * 2)) { + uint32_t reg = (address & 0xFE) >> 1; + switch (reg) + { + case S32X_SH2_INT_CTRL: + if (sh2 != mars->main) { + return (mars->sh2_regs[reg] & 0xFFF0) | mars->sh2_regs[S32X_SH2_SUB_INT]; + } + case S32X_SH2_STANDBY: + case S32X_SH2_HINT_COUNT: + return mars->sh2_regs[reg]; + case S32X_DREQ_LEN: + //supposedly the low two bits are always 0 here + //but it's convenient to use them to track trasnfer progress, so we just mask them here + return mars->regs[S32X_DREQ_LEN] & 0xFFFC; + case S32X_DREQ_FIFO: + //TODO: test what happens if you read from an empty FIFO + //TODO: test what happens if you read from the FIFO with 68S=0 + if (mars->regs[S32X_DREQ_CTRL] & BIT_DREQ_68S) { + if ((mars->regs[S32X_DREQ_CTRL] & BIT_DREQ_FULL) || mars->dreq_fifo_write != mars->dreq_fifo_read) { + uint16_t value = mars->dreq_fifo[mars->dreq_fifo_read++]; + mars->dreq_fifo_read &= 0x7; + mars->regs[S32X_DREQ_CTRL] &= ~BIT_DREQ_FULL; + mars->regs[S32X_DREQ_LEN]--; + if (mars->dreq_fifo_write == mars->dreq_fifo_read) { + //TODO: if/when edge vs level DREQ Is implemented + //generate a new edge here when the fifo is NOT empty + sh7095_clear_dreq0(mars->main); + sh7095_clear_dreq0(mars->sub); + } + if (!mars->regs[S32X_DREQ_LEN]) { + mars->regs[S32X_DREQ_CTRL] &= ~BIT_DREQ_68S; + } + return value; + } + } + return 0; + case S32X_PWM_WIDTH_M: + s32x_pwm_run(mars, sh2->cycles); + //TODO: test what happens when reading the FIFO status bits here when L & R don't match + return mars->regs[S32X_PWM_WIDTH_L] & mars->regs[S32X_PWM_WIDTH_R]; + case S32X_PWM_WIDTH_L: + case S32X_PWM_WIDTH_R: + s32x_pwm_run(mars, sh2->cycles); + default: + return mars->regs[reg]; + } + } else if (address >= 0x0004100) { + if (!(mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM)) { + sh2->cycles = mars->cur_sh2_target; + save_sh2_state(mars, sh2); + return 0xFFFF; + } + s32x_video_run(&mars->video, sh2->cycles / 3); + return s32x_video_sh2_read(address, &mars->video); + } + return 0xFFFF; +} + +uint8_t s32x_sh2_read_b(uint32_t address, void *vcontext) +{ + uint16_t val = s32x_sh2_read(address & ~1, vcontext); + if (address & 1) { + return val; + } + return val >> 8; +} + +//TODO: confirm which bits are actually writeable +static uint16_t reg_write_masks[S32X_NUM_REGS] = { + 0x8003, + 0x0003, + 0x0003, + 0x0007, + 0x00FF, + 0xFFFE, + 0x00FF, + 0xFFFF, + 0xFFFC, + [S32X_SEGA_TV] = 0x0001, + [S32X_COMM_0] = 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, + 0x000F, + 0x0FFF +}; + +static void check_cart_map_change(uint32_t reg, m68k_context *m68k, uint16_t changes) +{ + uint8_t aden_changed = reg == S32X_ADAPT_CTRL && (changes & BIT_ADEN_M68K); + uint8_t rv_changed = reg == S32X_DREQ_CTRL && (changes & BIT_DREQ_RV); + uint8_t bank_changed = reg == S32X_CART_BANK && (changes & S32X_BANK_MASK); + if (aden_changed || rv_changed || bank_changed) { + genesis_context *gen = m68k->system; + s32x *mars = gen->mars; + uint8_t cart_mapped_high = (mars->regs[S32X_ADAPT_CTRL] & BIT_ADEN_M68K) && !(mars->regs[S32X_DREQ_CTRL] & BIT_DREQ_RV); + if (cart_mapped_high) { + mars->main->mem_pointers[0] = (uint8_t *)gen->cart; + mars->sub->mem_pointers[0] = (uint8_t *)gen->cart; + m68k->mem_pointers[0] = NULL; + m68k->mem_pointers[1] = gen->cart; + // This is either for SRAM with the cart mapped low or unused + m68k->mem_pointers[3] = NULL; + uint32_t bank_start = (mars->regs[S32X_CART_BANK] & S32X_BANK_MASK) << 20; + const memmap_chunk *chunk = find_map_chunk(gen->save_type == SAVE_I2C ? bank_start + 2 : bank_start, &m68k->opts->gen, 0, NULL); + if (!chunk) { + m68k->mem_pointers[2] = NULL; + return; + } + uint32_t offset = bank_start - (chunk->start & ~chunk->mask); + offset &= chunk->mask; + if (chunk->flags & (MMAP_ONLY_ODD | MMAP_ONLY_EVEN)) { + offset >>= 1; + } + if (gen->mapper_type == MAPPER_SEGA_SRAM && chunk->write_16 == s32x_write_sram_area_w && (gen->bank_regs[0] & 3) == 1) { + gen->mapper_temp = ((uint8_t *)chunk->buffer) + offset; + m68k->mem_pointers[2] = NULL; + } else { + m68k->mem_pointers[2] = (uint16_t *)(((uint8_t *)chunk->buffer) + offset); + gen->mapper_temp = NULL; + } + } else { + m68k->mem_pointers[0] = gen->cart; + m68k->mem_pointers[1] = NULL; + m68k->mem_pointers[2] = NULL; + memmap_chunk *chunk = NULL; + for (uint32_t i = 0; i < m68k->opts->gen.memmap_chunks; i++) + { + const memmap_chunk *chunk = m68k->opts->gen.memmap + i; + if ((chunk->flags & MMAP_PTR_IDX) && chunk->ptr_index == 3) { + m68k->mem_pointers[3] = chunk->buffer; + break; + } else { + chunk = NULL; + } + } + if (gen->mapper_type == MAPPER_SEGA_SRAM) { + if ((gen->bank_regs[0] & 3) == 1) { + m68k->mem_pointers[3] = NULL; + if (chunk) { + gen->mapper_temp = chunk->buffer; + } + } else { + if (chunk) { + m68k->mem_pointers[3] = chunk->buffer; + } + gen->mapper_temp = NULL; + } + } else if (chunk) { + m68k->mem_pointers[3] = chunk->buffer; + } + } + if (mars->regs[S32X_ADAPT_CTRL] & BIT_ADEN_M68K) { + m68k->mem_pointers[9] = mars->vector_rom; + } else { + m68k->mem_pointers[9] = gen->cart; + } + if (aden_changed) { + m68k_invalidate_code_range(m68k, 0x0, 0x100); + } + if (bank_changed) { + m68k_invalidate_code_range(m68k, 0x900000, 0xA00000); + } + gen_update_z80_bank_pointer(gen); + } +} + +static void maybe_update_pwm_dreq(s32x *mars) +{ + if (!(mars->regs[S32X_PWM_CTRL] & BIT_PWM_RTP)) { + return; + } + uint8_t data_needed = 1; + switch (mars->regs[S32X_PWM_CTRL] & 3) + { + case 1: data_needed = !(mars->regs[S32X_PWM_WIDTH_L] & BIT_PWM_FULL); break; + case 2: data_needed = !(mars->regs[S32X_PWM_WIDTH_R] & BIT_PWM_FULL); break; + //TODO: what happens if the illegal 3 value is used + case 3: data_needed = 0; break; + } + switch (mars->regs[S32X_PWM_CTRL] >> 2 & 3) + { + case 1: data_needed = data_needed && !(mars->regs[S32X_PWM_WIDTH_R] & BIT_PWM_FULL); break; + case 2: data_needed = data_needed && !(mars->regs[S32X_PWM_WIDTH_L] & BIT_PWM_FULL); break; + //TODO: what happens if the illegal 3 value is used + case 3: data_needed = 0; break; + } + if (data_needed) { + sh7095_assert_dreq1(mars->main); + sh7095_assert_dreq1(mars->sub); + } else { + sh7095_clear_dreq1(mars->main); + sh7095_clear_dreq1(mars->sub); + } +} + +void s32x_68k_sysreg_write(uint32_t reg, m68k_context *m68k, s32x *mars, uint16_t mask, uint16_t value) +{ + uint16_t old = mars->regs[reg]; + uint16_t new = (old & ~mask) | (value & mask); + uint16_t changes = old ^ new; + switch(reg) + { + case S32X_ADAPT_CTRL: + if (changes & BIT_SH2_RESET) { + if (new & BIT_SH2_RESET) { + sh2_clear_reset(mars->main); + sh2_clear_reset(mars->sub); + } else { + sh2_assert_reset(mars->main); + sh2_assert_reset(mars->sub); + } + } + if (changes & BIT_ADEN_M68K) { + if (new & BIT_ADEN_M68K) { + mars->sh2_regs[S32X_SH2_INT_CTRL] |= BIT_ADEN_SH2; + } else { + mars->sh2_regs[S32X_SH2_INT_CTRL] &= ~BIT_ADEN_SH2; + } + } + if (changes & BIT_ADCT_FM) { + mars->sh2_regs[S32X_SH2_INT_CTRL] &= ~BIT_ADCT_FM; + mars->sh2_regs[S32X_SH2_INT_CTRL] |= new & BIT_ADCT_FM; + } + break; + case S32X_INT_CTRL: + if (changes & BIT_MAIN_INT) { + main_sh2_next_int(mars->main); + } + if (changes & BIT_SUB_INT) { + sub_sh2_next_int(mars->main); + } + break; + case S32X_DREQ_CTRL: + //RV changes handled below + if (changes & BIT_DREQ_68S) { + if (old & BIT_DREQ_68S) { + //unclear if FIFO is emptied, or if the full bit is just suppressed + new &= ~BIT_DREQ_FULL; + mars->dreq_fifo_write = mars->dreq_fifo_read = 0; + sh7095_clear_dreq0(mars->main); + sh7095_clear_dreq0(mars->sub); + } else if (((mars->dreq_fifo_write - mars->dreq_fifo_read) & 0x7) >= 4) { + sh7095_assert_dreq0(mars->main); + sh7095_assert_dreq0(mars->sub); + } + } + break; + case S32X_DREQ_LEN: + //force low bits to 0 on write + new &= 0xFFFC; + break; + case S32X_DREQ_FIFO: + //TODO: test what happens when you write to a full FIFO + //TODO: test what happens if you write to this when 68S is 0 + if (mars->regs[S32X_DREQ_CTRL] & BIT_DREQ_68S) { + mars->dreq_fifo[mars->dreq_fifo_write++] = value; + mars->dreq_fifo_write &= 0x7; + if (mars->regs[S32X_DREQ_CTRL] & BIT_DREQ_FULL) { + //treating this like the PWM FIFO and evicting the oldest word for now + mars->dreq_fifo_read++; + mars->dreq_fifo_read &= 0x7; + + } else if (mars->dreq_fifo_write == mars->dreq_fifo_read) { + mars->regs[S32X_DREQ_CTRL] |= BIT_DREQ_FULL; + } + if ((mars->regs[S32X_DREQ_CTRL] & BIT_DREQ_68S) + && ((mars->regs[S32X_DREQ_CTRL] & BIT_DREQ_FULL) || ((mars->dreq_fifo_write - mars->dreq_fifo_read) & 0x7) >= 4) + ) { + sh7095_assert_dreq0(mars->main); + sh7095_assert_dreq0(mars->sub); + } + } + break; + case S32X_PWM_WIDTH_M: + case S32X_PWM_WIDTH_L: + pwm_fifo_write(&mars->fifo_left, &mars->regs[S32X_PWM_WIDTH_L], value); + if (reg != S32X_PWM_WIDTH_M) { + maybe_update_pwm_dreq(mars); + return; + } + case S32X_PWM_WIDTH_R: + pwm_fifo_write(&mars->fifo_right, &mars->regs[S32X_PWM_WIDTH_R], value); + maybe_update_pwm_dreq(mars); + return; + } + mars->regs[reg] = new; + check_cart_map_change(reg, m68k, changes); +} + +void *s32x_68k_write(uint32_t address, void *vcontext, uint16_t value) +{ + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + s32x *mars = gen->mars; + s32x_run(mars, m68k->cycles); + if (address < 0xA15100 + (S32X_NUM_REGS * 2)) { + uint32_t reg = (address & 0xFF) >> 1; + uint16_t mask = reg_write_masks[reg]; + dprintf("32X 68K Write: %06X: %04X\n", address, value); + s32x_68k_sysreg_write(reg, m68k, mars, mask, value); + } else if (address >= 0xA15180) { + if (mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM) { + //writes are ignored when FM is set + return vcontext; + } + gen->bus_busy = 1; + for (;;) + { + s32x_run(mars, m68k->cycles); + uint32_t wait_cycles = s32x_video_68k_write(address, &mars->video, value); + if (wait_cycles) { + uint32_t target = m68k->cycles + wait_cycles; + if (target > m68k->sync_cycle) { + if (m68k->sync_cycle <= m68k->cycles) { + target = m68k->sync_cycle + 1; + } else { + target = m68k->sync_cycle; + } + } + m68k->cycles = target; +#ifdef NEW_CORE + m68k->sync_components(m68k, 0); +#else + m68k->opts->sync_components(m68k, 0); +#endif + } else { + break; + } + } + gen->bus_busy = 0; + } + return vcontext; +} + +void *s32x_68k_write_b(uint32_t address, void *vcontext, uint8_t value) +{ + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + s32x *mars = gen->mars; + s32x_run(mars, m68k->cycles); + if (address < 0xA15100 + (S32X_NUM_REGS * 2)) { + dprintf("32X 68K Write (byte): %06X: %02X\n", address, value); + uint32_t reg = (address & 0xFF) >> 1; + uint16_t mask = reg_write_masks[reg]; + uint16_t extended; + if (address & 1) { + extended = value; + mask &= 0x00FF;; + } else { + extended = value << 8; + mask &= 0xFF00; + } + s32x_68k_sysreg_write(reg, m68k, mars, mask, extended); + } else if (address >= 0xA15180) { + if (mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM) { + //writes are ignored when FM is set + return vcontext; + } + gen->bus_busy = 1; + for (;;) + { + s32x_run(mars, m68k->cycles); + uint32_t wait_cycles = s32x_video_68k_write_b(address, &mars->video, value); + if (wait_cycles) { + uint32_t target = m68k->cycles + wait_cycles; + if (target > m68k->sync_cycle) { + if (m68k->sync_cycle <= m68k->cycles) { + target = m68k->sync_cycle + 1; + } else { + target = m68k->sync_cycle; + } + } + m68k->cycles = target; +#ifdef NEW_CORE + m68k->sync_components(m68k, 0); +#else + m68k->opts->sync_components(m68k, 0); +#endif + } else { + break; + } + } + gen->bus_busy = 0; + } + return vcontext; +} + +//TODO: confirm which bits are actually writeable +static uint16_t sh2_write_masks[S32X_NUM_REGS] = { + 0x808F, //0 = interrupt mask + 0xFFFF, //2 = stand by change + 0x00FF, //4 = h count + [S32X_SEGA_TV] = 0x0001, + [S32X_COMM_0] = 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, + 0x0F8F, + 0x0FFF +}; + +static void s32x_sh2_sysreg_write(uint32_t reg, sh2_context *sh2, s32x *mars, uint16_t mask, uint16_t value) +{ + uint16_t *base = reg < S32X_NUM_SH2_REGS ? mars->sh2_regs : mars->regs; + uint16_t old = base[reg]; + uint16_t new = (old & ~mask) | (value & mask); + uint16_t changes = old ^ new; + switch (reg) + { + case S32X_SH2_INT_CTRL: + if (changes & BIT_ADCT_FM) { + mars->regs[S32X_ADAPT_CTRL] &= ~BIT_ADCT_FM; + mars->regs[S32X_ADAPT_CTRL] |= new & BIT_ADCT_FM; + } + s32x_video_run(&mars->video, sh2->cycles / 3); + mars->video.hen = (new & BIT_INTMASK_HEN) ? 1 : 0; + if (sh2->main) { + if (changes & S32X_INTEN_MASK) { + base[reg] = new; + main_sh2_next_int(sh2); + if (changes & BIT_INTMASK_HEN) { + sub_sh2_next_int(mars->sub); + } + } + } else { + uint16_t old_int = mars->sh2_regs[S32X_SH2_SUB_INT]; + uint16_t mask_int = mask & 0xF; + uint16_t new_int = (old_int & ~mask_int) | (value & mask_int); + changes = (old_int ^ new_int) | (changes & BIT_INTMASK_HEN); + if (changes) { + mars->sh2_regs[S32X_SH2_SUB_INT] = new_int; + sub_sh2_next_int(sh2); + if (changes & BIT_INTMASK_HEN) { + main_sh2_next_int(mars->main); + } + } + mask &= 0xFFF0; + new = (old & ~mask) | (value & mask); + } + break; + case S32X_SH2_HINT_COUNT: + s32x_video_run(&mars->video, sh2->cycles / 3); + mars->video.hint_count = new; + main_sh2_next_int(mars->main); + sub_sh2_next_int(mars->sub); + break; + case S32X_VINT_CLR: + s32x_video_run(&mars->video, sh2->cycles / 3); + if (sh2->main) { + mars->video.main_vint_pending = 0; + main_sh2_next_int(sh2); + } else { + mars->video.sub_vint_pending = 0; + sub_sh2_next_int(sh2); + } + break; + case S32X_HINT_CLR: + s32x_video_run(&mars->video, sh2->cycles / 3); + if (sh2->main) { + mars->video.main_hint_pending = 0; + main_sh2_next_int(sh2); + } else { + mars->video.sub_hint_pending = 0; + sub_sh2_next_int(sh2); + } + break; + case S32X_CMD_INT_CLR: + if (sh2->main) { + mars->regs[S32X_INT_CTRL] &= ~BIT_MAIN_INT; + main_sh2_next_int(sh2); + } else { + mars->regs[S32X_INT_CTRL] &= ~BIT_SUB_INT; + sub_sh2_next_int(sh2); + } + break; + case S32X_PWM_INT_CLR: + s32x_pwm_run(mars, sh2->cycles); + if (sh2->main) { + mars->pwm_main_int_pending = 0; + main_sh2_next_int(sh2); + } else { + mars->pwm_sub_int_pending = 0; + sub_sh2_next_int(sh2); + } + break; + case S32X_PWM_WIDTH_M: + case S32X_PWM_WIDTH_L: + s32x_pwm_run(mars, sh2->cycles); + pwm_fifo_write(&mars->fifo_left, &base[S32X_PWM_WIDTH_L], value); + if (reg != S32X_PWM_WIDTH_M) { + maybe_update_pwm_dreq(mars); + return; + } + case S32X_PWM_WIDTH_R: + s32x_pwm_run(mars, sh2->cycles); + pwm_fifo_write(&mars->fifo_right, &base[S32X_PWM_WIDTH_R], value); + maybe_update_pwm_dreq(mars); + return; + case S32X_PWM_CTRL: + s32x_pwm_run(mars, sh2->cycles); + if (changes & BIT_PWM_RTP) { + if (new & BIT_PWM_RTP) { + base[reg] = new; + maybe_update_pwm_dreq(mars); + } else { + sh7095_clear_dreq1(mars->main); + sh7095_clear_dreq1(mars->sub); + } + } + break; + case S32X_PWM_CYCLE: + s32x_pwm_run(mars, sh2->cycles); + break; + } + base[reg] = new; +} + +void *s32x_sh2_write(uint32_t address, void *vcontext, uint16_t value) +{ + sh2_context *sh2 = vcontext; + s32x *mars = sh2->system; + if (address < 0x0004000 + (S32X_NUM_REGS * 2)) { + if (sh2->main) { + sh2_run(mars->sub, sh2->cycles); + } + sh2->cycles += 3 * sh2->opts->gen.clock_divider; + uint32_t reg = (address & 0xFE) >> 1; + uint16_t mask = sh2_write_masks[reg]; + dprintf("32X SH2 %c Write: %06X: %04X\n", sh2 == mars->main ? 'M' : 'S', address, value); + s32x_sh2_sysreg_write(reg, sh2, mars, mask, value); + } else if (address >= 0x0004100) { + //SH2 writes seem to always go through for some reason, even when FM is clear + //have occasionally seen the writes be delayed, but not consistent + //needs more testing + sh2->cycles += 6 * sh2->opts->gen.clock_divider; + for (;;) + { + if (sh2->main) { + sh2_run(mars->sub, sh2->cycles); + } + s32x_video_run(&mars->video, sh2->cycles / 3); + uint32_t wait_cycles = s32x_video_sh2_write(address, &mars->video, value); + if (wait_cycles) { + //TODO: sync components + sh2->cycles += wait_cycles; + } else { + break; + } + } + } + return vcontext; +} + +void *s32x_sh2_write_b(uint32_t address, void *vcontext, uint8_t value) +{ + sh2_context *sh2 = vcontext; + s32x *mars = sh2->system; + if (sh2->main) { + sh2_run(mars->sub, sh2->cycles); + } + if (address < 0x0004000 + (S32X_NUM_REGS * 2)) { + uint32_t reg = (address & 0xFE) >> 1; + uint16_t mask = sh2_write_masks[reg]; + uint16_t extended; + if (sh2->main) { + sh2_run(mars->sub, sh2->cycles); + } + sh2->cycles += 3 * sh2->opts->gen.clock_divider; + if (address & 1) { + extended = value; + mask &= 0x00FF;; + } else { + extended = value << 8; + mask &= 0xFF00; + } + dprintf("32X SH2 Write: %06X: %04X\n", address, value); + s32x_sh2_sysreg_write(reg, sh2, mars, mask, extended); + } else if (address >= 0x0004100) { + //SH2 writes seem to always go through for some reason, even when FM is clear + //have occasionally seen the writes be delayed, but not consistent + //needs more testing + sh2->cycles += 6 * sh2->opts->gen.clock_divider; + for (;;) + { + if (sh2->main) { + sh2_run(mars->sub, sh2->cycles); + } + s32x_video_run(&mars->video, sh2->cycles / 3); + uint32_t wait_cycles = s32x_video_sh2_write_b(address, &mars->video, value); + if (wait_cycles) { + //TODO: sync components + sh2->cycles += wait_cycles; + } else { + break; + } + } + } + return vcontext; +} + +static uint16_t *get_68K_vector_rom(uint32_t size) +{ + if (size < 0x100) { + size = 0x100; + } + uint16_t *ret = calloc(1, size); + char *m68k_path = tern_find_path_default(config, "system\0s32x_68k_bios\0", (tern_val){.ptrval = "32X_G_BIOS.bin"}, TVAL_PTR).ptrval; + FILE *f = fopen(m68k_path, "rb"); + if (f) { + fread(ret, 1, 0x100, f); + byteswap_rom(0x100, ret); + fclose(f); + } else { + warning("32X 68K BIOS not found at %s. Some games may function without it, but it is needed for full compatibility\n", m68k_path); + ret[0] = ret[1] = 0; + uint32_t vector = 0x880200; + for (int i = 0; i < 47; i++) + { + ret[i * 2] = vector >> 16; + ret[i * 2 + 1] = vector; + vector += 6; + } + //TODO: fake the subroutines maybe? + } + for (uint32_t i = 0x100; i < size - 0xFF; i += 0x100) + { + memcpy(ret + i / 2, ret, 0x100); + } + return ret; +} + +void *s32x_write_hint(uint32_t address, void *vcontext, uint16_t value) +{ + if (address >= 0x70 && address < 0x74) { + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + s32x *mars = gen->mars; + uint8_t cart_mapped_high = (mars->regs[S32X_ADAPT_CTRL] & BIT_ADEN_M68K) && !(mars->regs[S32X_DREQ_CTRL] & BIT_DREQ_RV); + if (cart_mapped_high) { + mars->vector_rom[address >> 1] = value; + } + } + return vcontext; +} + +void *s32x_write_hint_b(uint32_t address, void *vcontext, uint8_t value) +{ + if (address >= 0x70 && address < 0x74) { + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + s32x *mars = gen->mars; + uint8_t cart_mapped_high = (mars->regs[S32X_ADAPT_CTRL] & BIT_ADEN_M68K) && !(mars->regs[S32X_DREQ_CTRL] & BIT_DREQ_RV); + if (cart_mapped_high) { + if (address & 1) { + mars->vector_rom[address >> 1] &= 0xFF00; + mars->vector_rom[address >> 1] |= value; + } else { + mars->vector_rom[address >> 1] &= 0x00FF; + mars->vector_rom[address >> 1] |= value << 8; + } + } + } + return vcontext; +} + +void *s32x_fb_write_w(uint32_t address, void *vcontext, uint16_t value) +{ + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + s32x *mars = gen->mars; + s32x_run(mars, m68k->cycles); + if (mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM) { + //TODO: confirm that this actually behaves like register writes + return vcontext; + } + s32x_video_fb_write_w(address, &mars->video, value); + return vcontext; +} + +void *s32x_fb_write_b(uint32_t address, void *vcontext, uint8_t value) +{ + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + s32x *mars = gen->mars; + s32x_run(mars, m68k->cycles); + if (mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM) { + //TODO: confirm that this actually behaves like register writes + return vcontext; + } + //byte writes behave as if they were written to the overwrite area + s32x_video_overwrite_write_b(address, &mars->video, value); + return vcontext; +} + +uint16_t s32x_fb_read_w(uint32_t address, void *vcontext) +{ + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + s32x *mars = gen->mars; + s32x_run(mars, m68k->cycles); + while (mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM) { + gen->bus_busy = 1; + //FIXME: make this continue exactly when FM Is flipped + m68k->cycles += MAX_SH2_CYCLES / 3; +#ifdef NEW_CORE + m68k->sync_components(m68k, 0); +#else + m68k->opts->sync_components(m68k, 0); +#endif + } + gen->bus_busy = 0; + return s32x_video_fb_read_w(address, &mars->video); +} + +uint8_t s32x_fb_read_b(uint32_t address, void *vcontext) +{ + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + s32x *mars = gen->mars; + s32x_run(mars, m68k->cycles); + while (mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM) { + gen->bus_busy = 1; + //FIXME: make this continue exactly when FM Is flipped + m68k->cycles += MAX_SH2_CYCLES / 3; +#ifdef NEW_CORE + m68k->sync_components(m68k, 0); +#else + m68k->opts->sync_components(m68k, 0); +#endif + } + gen->bus_busy = 0; + return s32x_video_fb_read_b(address, &mars->video); +} + +void *s32x_sh2_fb_write_w(uint32_t address, void *vcontext, uint16_t value) +{ + sh2_context *sh2 = vcontext; + s32x *mars = sh2->system; + if (sh2->main) { + sh2_run(mars->sub, sh2->cycles); + } + if (!(mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM)) { + sh2->cycles = mars->cur_sh2_target; + save_sh2_state(mars, sh2); + return vcontext; + } + s32x_video_run(&mars->video, sh2->cycles / 3); + s32x_video_fb_write_w(address, &mars->video, value); + return vcontext; +} + +void *s32x_sh2_fb_write_b(uint32_t address, void *vcontext, uint8_t value) +{ + sh2_context *sh2 = vcontext; + s32x *mars = sh2->system; + if (sh2->main) { + sh2_run(mars->sub, sh2->cycles); + } + if (!(mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM)) { + sh2->cycles = mars->cur_sh2_target; + save_sh2_state(mars, sh2); + return vcontext; + } + s32x_video_run(&mars->video, sh2->cycles / 3); + //byte writes behave as if they were written to the overwrite area + s32x_video_overwrite_write_b(address, &mars->video, value); + return vcontext; +} + +uint16_t s32x_sh2_fb_read_w(uint32_t address, void *vcontext) +{ + sh2_context *sh2 = vcontext; + s32x *mars = sh2->system; + if (sh2->main) { + sh2_run(mars->sub, sh2->cycles); + } + if (!(mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM)) { + sh2->cycles = mars->cur_sh2_target; + save_sh2_state(mars, sh2); + return 0xFFFF; + } + s32x_video_run(&mars->video, sh2->cycles / 3); + return s32x_video_fb_read_w(address, &mars->video); +} + +uint8_t s32x_sh2_fb_read_b(uint32_t address, void *vcontext) +{ + sh2_context *sh2 = vcontext; + s32x *mars = sh2->system; + if (sh2->main) { + sh2_run(mars->sub, sh2->cycles); + } + if (!(mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM)) { + sh2->cycles = mars->cur_sh2_target; + save_sh2_state(mars, sh2); + return 0xFF; + } + s32x_video_run(&mars->video, sh2->cycles / 3); + return s32x_video_fb_read_b(address, &mars->video); +} + +void *s32x_overwrite_write_w(uint32_t address, void *vcontext, uint16_t value) +{ + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + s32x *mars = gen->mars; + s32x_run(mars, m68k->cycles); + while (mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM) { + gen->bus_busy = 1; + //FIXME: make this continue exactly when FM Is flipped + m68k->cycles += MAX_SH2_CYCLES / 3; +#ifdef NEW_CORE + m68k->sync_components(m68k, 0); +#else + m68k->opts->sync_components(m68k, 0); +#endif + } + gen->bus_busy = 0; + s32x_video_overwrite_write_w(address, &mars->video, value); + return vcontext; +} + +void *s32x_overwrite_write_b(uint32_t address, void *vcontext, uint8_t value) +{ + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + s32x *mars = gen->mars; + s32x_run(mars, m68k->cycles); + while (mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM) { + gen->bus_busy = 1; + //FIXME: make this continue exactly when FM Is flipped + m68k->cycles += MAX_SH2_CYCLES / 3; +#ifdef NEW_CORE + m68k->sync_components(m68k, 0); +#else + m68k->opts->sync_components(m68k, 0); +#endif + } + gen->bus_busy = 0; + s32x_video_overwrite_write_b(address, &mars->video, value); + return vcontext; +} + +void *s32x_sh2_overwrite_write_w(uint32_t address, void *vcontext, uint16_t value) +{ + sh2_context *sh2 = vcontext; + s32x *mars = sh2->system; + if (sh2->main) { + sh2_run(mars->sub, sh2->cycles); + } + if (!(mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM)) { + sh2->cycles = mars->cur_sh2_target; + save_sh2_state(mars, sh2); + return vcontext; + } + s32x_video_run(&mars->video, sh2->cycles / 3); + s32x_video_overwrite_write_w(address, &mars->video, value); + return vcontext; +} + +void *s32x_sh2_overwrite_write_b(uint32_t address, void *vcontext, uint8_t value) +{ + sh2_context *sh2 = vcontext; + s32x *mars = sh2->system; + if (sh2->main) { + sh2_run(mars->sub, sh2->cycles); + } + if (!(mars->regs[S32X_ADAPT_CTRL] & BIT_ADCT_FM)) { + sh2->cycles = mars->cur_sh2_target; + save_sh2_state(mars, sh2); + return vcontext; + } + s32x_video_run(&mars->video, sh2->cycles / 3); + s32x_video_overwrite_write_b(address, &mars->video, value); + return vcontext; +} + +uint32_t s32x_sh2_read_external_32(uint32_t address, sh2_context *sh2) +{ + address &= 0x7FFFFFF; + uint32_t ret; + if (address >= 0x6000000 && address < 0x6040000) { + //SDRAM access always does a 16-byte burst, but a single burst can + //satisfy both words of the longword read + address &= 0x3FFFF; + address >>= 1; + s32x *mars = sh2->system; + ret = mars->sdram[address] << 16; + ret |= mars->sdram[address | 1] ; + sh2->cycles += 10 * sh2->opts->gen.clock_divider; + } else { + ret = sh2->read16[1](address, sh2) << 16; + ret |= sh2->read16[1](address | 2, sh2); + } + return ret; +} + +void s32x_sh2_write_external_32(uint32_t address, sh2_context *sh2, uint32_t value) +{ + address &= 0x7FFFFFF; + if (address >= 0x6000000 && address < 0x6040000) { + //this also seems optimized for the 32-bit case despite the 16-bit bus + address &= 0x3FFFF; + address >>= 1; + s32x *mars = sh2->system; + mars->sdram[address] = value >> 16; + mars->sdram[address | 1] = value; + //this seems too fast, but I get way too low values on 32xspd.32x otherwise + sh2->cycles += sh2->opts->gen.clock_divider; + } else { + sh2->write16[1](address, sh2, value >> 16); + sh2->write16[1](address | 2, sh2, value); + } +} + +//TODO: share these with genesis.c +#define MCLKS_NTSC 53693175 +#define MCLKS_PAL 53203395 + +s32x *alloc_32x(system_media *media, uint8_t pal, uint8_t cd_boot) +{ + static const memmap_chunk base_sh2_map[] = { + {0x6000000, 0x6040000, .mask = 0x3FFFF, .flags = MMAP_READ | MMAP_WRITE | MMAP_CODE, + //should be a 12-cycle burst, but I need 10 to get close to the right results in 32xspd.32x + .read_cycles = 10, .write_cycles = 1, .burst_cycles = 10}, + {0x4000000, 0x4020000, .mask = 0x7FFFFFF, .read_16 = s32x_sh2_fb_read_w, .write_16 = s32x_sh2_fb_write_w, + .read_8 = s32x_sh2_fb_read_b, .write_8 = s32x_sh2_fb_write_b}, + {0x4020000, 0x4040000, .mask = 0x7FFFFFF, .read_16 = s32x_sh2_fb_read_w, .write_16 = s32x_sh2_overwrite_write_w, + .read_8 = s32x_sh2_fb_read_b, .write_8 = s32x_sh2_overwrite_write_b}, + {0x2000000, 0x2400000, .mask = 0x3FFFFF, .flags = MMAP_READ | MMAP_PTR_IDX | MMAP_AUX_BUFF, .ptr_index = 0, + .read_cycles = 6, .write_cycles = 3, .burst_cycles = 6 * 8}, + {0x0004000, 0x0004400, .mask = 0x7FFFFFF, .read_16 = s32x_sh2_read, .write_16 = s32x_sh2_write, + .read_8 = s32x_sh2_read_b, .write_8 = s32x_sh2_write_b}, + {0x0000000, 0x0004000, .mask = 0x7FFFFFF, .flags = MMAP_READ, + .read_cycles = 3, .write_cycles = 3, .burst_cycles = 3 * 8}, + }; + static const size_t num_chunks = sizeof(base_sh2_map)/sizeof(*base_sh2_map); + s32x *ret = calloc(1, sizeof(s32x)); + ret->sdram = aligned_calloc(128*1024, sizeof(uint16_t), 16); + + memmap_chunk *main_map = calloc(num_chunks, sizeof(memmap_chunk)); + memcpy(main_map, base_sh2_map, sizeof(base_sh2_map)); + main_map[0].buffer = ret->sdram; + if (cd_boot) { + //TODO: BRAM cart support? + main_map[3].flags &= ~MMAP_AUX_BUFF; + } else { + main_map[3].buffer = media->buffer; + main_map[3].mask &= nearest_pow2(media->size) - 1; + } + main_map[5].buffer = aligned_calloc(1, main_map[5].end, 16); + char *main_path = tern_find_path_default(config, "system\0s32x_main_bios\0", (tern_val){.ptrval = "32X_M_BIOS.bin"}, TVAL_PTR).ptrval; + FILE *f = fopen(main_path, "rb"); + if (f) { + fread(main_map[5].buffer, 1, main_map[5].end, f); + byteswap_rom(main_map[5].end, main_map[5].buffer); + fclose(f); + } else { + warning("32X Main SH2 BIOS not found at %s. 32X will not function correctly until you fix your config\n", main_path); + } + sh2_options *main_opts = calloc(1, sizeof(sh2_options)); + init_sh2_opts(main_opts, main_map, num_chunks); + ret->main = init_sh2_context(main_opts, main_sh2_next_int); + sh7095_setup(ret->main); + ret->main->sync_cycle = 0xFFFFFFFF; + ret->main->system = ret; + ret->main->main = 1; + ret->main_tmp = calloc(1, sizeof(sh2_context)); + ret->main->write32[0] = ret->main->write32[1] = s32x_sh2_write_external_32; + ret->main->read32[0] = ret->main->read32[1] = s32x_sh2_read_external_32; + + memmap_chunk *sub_map = calloc(num_chunks, sizeof(memmap_chunk)); + memcpy(sub_map, base_sh2_map, sizeof(base_sh2_map)); + sub_map[0].buffer = ret->sdram; + if (cd_boot) { + //TODO: BRAM cart support? + sub_map[3].flags &= ~MMAP_AUX_BUFF; + } else { + sub_map[3].buffer = media->buffer; + sub_map[3].mask &= nearest_pow2(media->size) - 1; + + } + sub_map[5].buffer = aligned_calloc(1, sub_map[5].end, 16); + char *sub_path = tern_find_path_default(config, "system\0s32x_sub_bios\0", (tern_val){.ptrval = "32X_S_BIOS.bin"}, TVAL_PTR).ptrval; + f = fopen(sub_path, "rb"); + if (f) { + fread(sub_map[5].buffer, 1, sub_map[5].end, f); + byteswap_rom(sub_map[5].end, sub_map[5].buffer); + fclose(f); + } else { + warning("32X Sub SH2 BIOS not found at %s. 32X will not function correctly until you fix your config\n", sub_path); + } + sh2_options *sub_opts = calloc(1, sizeof(sh2_options)); + init_sh2_opts(sub_opts, sub_map, num_chunks); + ret->sub = init_sh2_context(sub_opts, sub_sh2_next_int); + sh7095_setup(ret->sub); + ret->sub->sync_cycle = 0xFFFFFFFF; + ret->sub->system = ret; + ret->sub_tmp = calloc(1, sizeof(sh2_context)); + ret->sub->write32[0] = ret->sub->write32[1] = s32x_sh2_write_external_32; + ret->sub->read32[0] = ret->sub->read32[1] = s32x_sh2_read_external_32; + + //hook up main/sub SCI + sh7095_periph *p = ret->main->periph_state; + p->sci_handler_data = ret->sub; + p->transmit_handler = sh7095_sci_to_sh7095_sci; + p = ret->sub->periph_state; + p->sci_handler_data = ret->main; + p->transmit_handler = sh7095_sci_to_sh7095_sci; + + sh2_assert_reset(ret->main); + sh2_assert_reset(ret->sub); + sh2_clear_reset(ret->main); + sh2_clear_reset(ret->sub); + s32x_video_init(&ret->video, pal); + ret->rom = media->buffer; + ret->regs[S32X_ADAPT_CTRL] = 0x0082; + ret->regs[S32X_PWM_WIDTH_L] = BIT_PWM_EMPTY; + ret->regs[S32X_PWM_WIDTH_R] = BIT_PWM_EMPTY; + ret->vector_rom = get_68K_vector_rom(media->size); + if (cd_boot) { + ret->sh2_regs[S32X_SH2_INT_CTRL] |= BIT_CART_SH2; + } + ret->pwm = render_audio_source("PWM", (pal ? MCLKS_PAL : MCLKS_NTSC) * 3, 7 * PWM_DECIMATE, 2); + return ret; +} + +void free_32x(s32x *mars) +{ + render_free_source(mars->pwm); + free(mars->vector_rom); + s32x_video_free(&mars->video); + sh7095_free(mars->sub); + sh7095_free(mars->main); + aligned_free(mars->main->opts->gen.memmap[5].buffer); + aligned_free(mars->sub->opts->gen.memmap[5].buffer); + free(mars->main->opts); + free(mars->sub->opts); + sh2_free(mars->main); + sh2_free(mars->sub); + aligned_free(mars->sdram); + free(mars); +} + diff --git a/32x.h b/32x.h new file mode 100644 index 0000000..19161c7 --- /dev/null +++ b/32x.h @@ -0,0 +1,139 @@ +#ifndef S32X_H_ +#define S32X_H_ +#include +#include "sh2.h" +#include "32x_video.h" +#include "oscilloscope.h" +#include "render_audio.h" + +enum { + S32X_ADAPT_CTRL, + S32X_INT_CTRL, + S32X_CART_BANK, + S32X_DREQ_CTRL, + S32X_DREQ_SRC_HI, + S32X_DREQ_SRC_LO, + S32X_DREQ_DST_HI, + S32X_DREQ_DST_LO, + S32X_DREQ_LEN, + S32X_DREQ_FIFO, + S32X_VRES_INT_CLR, + S32X_VINT_CLR, + S32X_HINT_CLR, + S32X_SEGA_TV, + S32X_PWM_INT_CLR, + S32X_REG_1E, + S32X_COMM_0, + S32X_COMM_1, + S32X_COMM_2, + S32X_COMM_3, + S32X_COMM_4, + S32X_COMM_5, + S32X_COMM_6, + S32X_COMM_7, + S32X_PWM_CTRL, + S32X_PWM_CYCLE, + S32X_PWM_WIDTH_L, + S32X_PWM_WIDTH_R, + S32X_PWM_WIDTH_M, + S32X_NUM_REGS, + S32X_CMD_INT_CLR = S32X_SEGA_TV +}; + +enum { + S32X_SH2_INT_CTRL, + S32X_SH2_STANDBY, + S32X_SH2_HINT_COUNT, + S32X_SH2_SUB_INT, + S32X_NUM_SH2_REGS = S32X_SH2_SUB_INT +}; + +#define BIT_ADEN_M68K 0x0001 +#define BIT_ADCT_FM 0x8000 +#define BIT_CART_SH2 0x0100 +#define BIT_ADEN_SH2 0x0200 +#define BIT_MAIN_INT 0x0001 +#define BIT_SUB_INT 0x0002 +#define BIT_INTMASK_HEN 0x0080 +#define BIT_VERT_INT_EN 0x0008 +#define BIT_HORZ_INT_EN 0x0004 +#define BIT_CMD_INT_EN 0x0002 +#define BIT_PWM_INT_EN 0x0001 +#define BIT_SH2_RESET 0x0002 +#define BIT_DREQ_FULL 0x0080 +#define BIT_DREQ_68S 0x0004 +#define BIT_DREQ_RV 0x0001 +#define BIT_PWM_RTP 0x0080 +#define BIT_PWM_FULL 0x8000 +#define BIT_PWM_EMPTY 0x4000 +#define S32X_BANK_MASK 0x0003 +#define S32X_INTEN_MASK 0x000F +#define S32X_PWM_LRMD 0x000F + +typedef struct { + uint16_t fifo[3]; + uint8_t write; + uint8_t read; +} pwm_fifo; + +void pwm_fifo_write(pwm_fifo *fifo, uint16_t *status, uint16_t value); +void pwm_fifo_read(pwm_fifo *fifo, uint16_t *status, uint16_t cycle, int16_t *out); + +typedef struct { + void *gen; + sh2_context *main; + sh2_context *sub; + sh2_context *main_tmp; + sh2_context *sub_tmp; + uint16_t *sdram; + uint16_t *rom; + uint16_t *vector_rom; + audio_source *pwm; + oscilloscope *scope; + s32x_video video; + uint32_t pwm_cycle; + uint32_t cur_sh2_target; + uint16_t regs[S32X_NUM_REGS]; + uint16_t sh2_regs[S32X_SH2_SUB_INT+1]; + uint16_t dreq_fifo[8]; + pwm_fifo fifo_left; + pwm_fifo fifo_right; + int16_t pwm_left; + int16_t pwm_right; + int16_t pwm_left_accum; + int16_t pwm_right_accum; + uint16_t pwm_counter; + uint8_t pwm_decimate; + uint8_t pwm_timer; + uint8_t pwm_main_int_pending; + uint8_t pwm_sub_int_pending; + uint8_t scope_left; + uint8_t scope_right; + uint8_t dreq_fifo_write; + uint8_t dreq_fifo_read; + uint8_t main_enter_debugger; + uint8_t sub_enter_debugger; + uint8_t saved_sh2_state; +} s32x; + +s32x *alloc_32x(system_media *media, uint8_t pal, uint8_t cd_boot); +void free_32x(s32x *mars); +void s32x_run(s32x *mars, uint32_t target); +void s32x_adjust_cycles(s32x *mars, uint32_t deduction); +void s32x_enable_scope(s32x *mars, oscilloscope *scope, uint32_t main_clock); +void s32x_set_speed(s32x *mars, uint32_t main_clock); +uint16_t s32x_68k_read(uint32_t address, void *vcontext); +void *s32x_68k_write(uint32_t address, void *vcontext, uint16_t value); +uint8_t s32x_68k_read_b(uint32_t address, void *vcontext); +void *s32x_68k_write_b(uint32_t address, void *vcontext, uint8_t value); +void *s32x_write_hint(uint32_t address, void *vcontext, uint16_t value); +void *s32x_write_hint_b(uint32_t address, void *vcontext, uint8_t value); +void *s32x_fb_write_w(uint32_t address, void *vcontext, uint16_t value); +void *s32x_fb_write_b(uint32_t address, void *vcontext, uint8_t value); +uint16_t s32x_fb_read_w(uint32_t address, void *vcontext); +uint8_t s32x_fb_read_b(uint32_t address, void *vcontext); +void *s32x_overwrite_write_w(uint32_t address, void *vcontext, uint16_t value); +void *s32x_overwrite_write_b(uint32_t address, void *vcontext, uint8_t value); + + +#endif //S32X_H_ diff --git a/32x_video.c b/32x_video.c new file mode 100644 index 0000000..cb9486e --- /dev/null +++ b/32x_video.c @@ -0,0 +1,849 @@ +#include +#include +#include "32x_video.h" +#include "vdp.h" +#include "render.h" + +#ifdef DO_DEBUG_PRINT +#define dprintf printf +#else +#define dprintf +#endif + +void s32x_video_init(s32x_video *vid, uint8_t pal) +{ + vid->front = calloc(256*1024, sizeof(uint8_t)); + vid->back = vid->front + 128*1024; + memset(vid->regs, 0, sizeof(vid->regs)); + if (!pal) { + vid->regs[S32X_VID_MODE] |= S32X_VID_BIT_PAL; + } +} + +void s32x_video_free(s32x_video *vid) +{ + if (vid->front > vid->back) { + free(vid->back); + } else { + free(vid->front); + } +} + +#define MCLKS_PIXEL 8 +#define HSYNC_START 368 +#define HSYNC_END (HSYNC_START+17*2) +#define HBLANK_START 356 +#define LINE_END 420 + +static uint32_t mclks_pixel[] = { + 10, 9, 10, 10, 10, 10, 10, 10, + 9, 9, 10, 10, 10, 10, 10, 10, + 9, 9, 10, 10, 10, 10, 10, 10, + 9, 9, 10, 10, 10, 10, 10, 10, + 10, 9 +}; + +//manual says 3, but they must mean SH2 clocks and not MCLKS +//3 SH2 clocks = 1 68K clock = 7 MCLKs +#define MCLKS_FILL_WORD 7 +#define MCLKS_FILL_LAST (MCLKS_FILL_WORD+16) + +void s32x_video_run(s32x_video *vid, uint32_t target) +{ + if (vid->cycle < target) { + uint32_t delta = target - vid->cycle; + uint32_t lines = delta / MCLKS_LINE; + uint32_t rest = delta % MCLKS_LINE; + uint16_t vblank_start = 224, frame_end; + if (!(vid->regs[S32X_VID_MODE] & S32X_VID_BIT_PAL)) { + frame_end = 313; + if (vid->regs[S32X_VID_MODE] & S32X_VID_BIT_V240) { + vblank_start = 240; + } + } else { + frame_end = 262; + } + for (uint32_t hint_lines = lines; hint_lines > 0;) + { + if (hint_lines > vid->hint_counter) { + if (vid->hen || (vid->vcounter + vid->hint_counter + 1 < vblank_start) || (!vid->hint_counter == vid->vcounter == (frame_end - 1))) { + vid->main_hint_pending = vid->sub_hint_pending = 1; + } + hint_lines -= vid->hint_counter + 1; + vid->hint_counter = vid->hint_count; + } else { + if (vid->vcounter < frame_end - 1 && hint_lines + vid->vcounter >= frame_end - 1) { + vid->hint_counter = vid->hint_count; + hint_lines -= frame_end - 1 - vid->vcounter; + } else { + vid->hint_counter -= hint_lines; + hint_lines = 0; + } + } + } + uint16_t line_start = vid->vcounter; + while (rest >= MCLKS_PIXEL) { + if (vid->hcounter < HSYNC_START) { + uint16_t new = vid->hcounter + rest / MCLKS_PIXEL; + if (vid->hcounter < HBLANK_START && new >= HBLANK_START && (vid->hen || vid->vcounter < (vblank_start - 1) || vid->vcounter == (frame_end - 1))) { + if (vid->hint_counter) { + vid->hint_counter--; + } else { + vid->main_hint_pending = vid->sub_hint_pending = 1; + vid->hint_counter = vid->hint_count; + } + } + + if (new > HSYNC_START) { + rest -= (HSYNC_START - vid->hcounter) * MCLKS_PIXEL; + vid->hcounter = HSYNC_START; + } else { + rest -= (new - vid->hcounter) * MCLKS_PIXEL; + vid->hcounter = new; + } + } else if (vid->hcounter >= HSYNC_END) { + uint16_t new = vid->hcounter + rest / MCLKS_PIXEL; + if (new >= LINE_END) { + rest -= (LINE_END - vid->hcounter) * MCLKS_PIXEL; + vid->hcounter = 0; + lines++; + } else { + rest -= (new - vid->hcounter) * MCLKS_PIXEL; + vid->hcounter = new; + } + } else { + uint16_t old = vid->hcounter; + while (vid->hcounter < HSYNC_END && rest >= mclks_pixel[vid->hcounter-HSYNC_START]) + { + rest -= mclks_pixel[vid->hcounter-HSYNC_START]; + vid->hcounter++; + } + if (old == vid->hcounter) { + //no progress made, rest > MCLKS_PIXEL, but not one of the adjusted pixels + break; + } + } + } + if (lines) { + vid->vcounter += lines; + if (vid->vcounter >= frame_end) { + vid->vcounter -= frame_end; + } + if (vid->vcounter >= vblank_start) { + if (!(vid->regs[S32X_VID_FB_CTRL] & S32X_VID_BIT_VBLK)) { + vid->regs[S32X_VID_FB_CTRL] |= S32X_VID_BIT_VBLK; + vid->main_vint_pending = 1; + vid->sub_vint_pending = 1; + if (vid->flip_pending) { + vid->flip_pending = 0; + uint8_t *tmp = vid->front; + vid->front = vid->back; + vid->back = tmp; + vid->regs[S32X_VID_FB_CTRL] ^= S32X_VID_BIT_FS; + } + } + } else { + vid->regs[S32X_VID_FB_CTRL] &= ~S32X_VID_BIT_VBLK; + if (line_start < vblank_start && lines >= frame_end - line_start) { + //passed through vblank and wrapped around + vid->main_vint_pending = 1; + vid->sub_vint_pending = 1; + if (vid->flip_pending) { + vid->flip_pending = 0; + uint8_t *tmp = vid->front; + vid->front = vid->back; + vid->back = tmp; + vid->regs[S32X_VID_FB_CTRL] ^= S32X_VID_BIT_FS; + } + } + } + } + if (vid->hcounter >= HBLANK_START) { + vid->regs[S32X_VID_FB_CTRL] |= S32X_VID_BIT_HBLK; + } else { + vid->regs[S32X_VID_FB_CTRL] &= ~S32X_VID_BIT_HBLK; + } + //run fill + delta = target - rest - vid->cycle + vid->fill_remainder; + if (vid->regs[S32X_VID_FB_CTRL] & S32X_VID_BIT_FEN) { + uint8_t count = vid->fill_count; + uint32_t address = vid->regs[S32X_VID_FILL_START] << 1; + uint8_t data_hi = vid->regs[S32X_VID_FILL_DATA] >> 8; + uint8_t data_lo = vid->regs[S32X_VID_FILL_DATA]; + uint32_t min_delta = count ? MCLKS_FILL_WORD : MCLKS_FILL_LAST; + while (delta >= min_delta) + { + vid->back[address] = data_hi; + vid->back[address|1] = data_lo; + address = (address & 0x1FE00) | ((address + 2) & 0x1FE); + if (count) { + --count; + delta -= MCLKS_FILL_WORD; + if (!count) { + min_delta = MCLKS_FILL_LAST; + } + } else { + vid->regs[S32X_VID_FB_CTRL] &= ~S32X_VID_BIT_FEN; + delta -= MCLKS_FILL_LAST; + break; + } + } + vid->fill_count = count; + vid->regs[S32X_VID_FILL_START] = address >> 1; + vid->fill_remainder = delta; + } + if ( + (vid->regs[S32X_VID_MODE] & S32X_VID_MODE_MASK) == 0 + || (vid->regs[S32X_VID_FB_CTRL] & (S32X_VID_BIT_VBLK|S32X_VID_BIT_HBLK)) + ) { + // palette access allowed during H or V blanking or when display is forcibly blanked (mode = 0) + vid->regs[S32X_VID_FB_CTRL] |= S32X_VID_BIT_PEN; + } else { + vid->regs[S32X_VID_FB_CTRL] &= ~S32X_VID_BIT_PEN; + } + vid->cycle = target - rest; + } +} + +static pixel_t map_32x_color(uint16_t color) +{ + return render_map_color(color << 3 & 0xF8, color >> 2 & 0xF8, color >> 7 & 0xF8); +} + +static void s32x_composite_indexed_h32(s32x_video *vid, pixel_t *output, uint8_t *compositebuf, uint32_t line_start) +{ + uint8_t *cur = vid->front + line_start + 319; + uint16_t pri = (vid->regs[S32X_VID_MODE] & S32X_VID_BIT_PRI) << 8; + for (pixel_t *dst = output + 319; dst >= output; dst--) + { + uint16_t color = vid->palette[*(cur--)]; + uint16_t on_top = (color & 0x8000) ^ pri; + if (on_top) { + *dst = map_32x_color(color); + } else { + uint32_t h32_pos = (dst - output) * 0x40000 / 5; + uint32_t base_index = h32_pos >> 16; + h32_pos &= 0xFFFF; + if (h32_pos > 0xE000) { + base_index++; + } else if (h32_pos >= 0x1000) { + //mix + uint8_t first_opaque = compositebuf[base_index] & 0xF; + uint8_t second_opaque = compositebuf[base_index+1] & 0xF; + if (first_opaque && second_opaque) { + *dst = mix_colors(output[base_index], output[base_index + 1], h32_pos); + } else if (first_opaque) { + *dst = mix_colors(output[base_index], map_32x_color(color), h32_pos); + } else if (second_opaque) { + *dst = mix_colors(map_32x_color(color), output[base_index + 1], h32_pos); + } else { + *dst = map_32x_color(color); + } + continue; + } + if (compositebuf[base_index] & 0xF) { + *dst = output[base_index]; + } else { + *dst = map_32x_color(color); + } + } + } +} + +static void s32x_composite_indexed(s32x_video *vid, pixel_t *output, uint8_t *compositebuf, uint32_t line_start, uint8_t is_h40) +{ + if (vid->regs[S32X_VID_SHIFT] & 1) { + line_start += 1; + } + if (!is_h40) { + s32x_composite_indexed_h32(vid, output, compositebuf, line_start); + return; + } + uint8_t *cur = vid->front + line_start; + uint16_t pri = (vid->regs[S32X_VID_MODE] & S32X_VID_BIT_PRI) << 8; + for (pixel_t *end = output + 320; output < end; output++) + { + uint16_t color = vid->palette[*(cur++)]; + uint16_t on_top = (color & 0x8000) ^ pri; + if (on_top || !(*compositebuf & 0xF)) { + *output = map_32x_color(color); + } + compositebuf++; + } +} + +static void s32x_composite_direct_h32(s32x_video *vid, pixel_t *output, uint8_t *compositebuf, uint32_t line_start) +{ + if (vid->regs[S32X_VID_SHIFT] & 1) { + line_start += 1; + } + uint8_t *cur = vid->front + line_start + 319 * 2; + uint16_t pri = (vid->regs[S32X_VID_MODE] & S32X_VID_BIT_PRI) << 8; + for (pixel_t *dst = output + 319; dst >= output; dst--) + { + uint16_t color = *(cur--); + color |= *(cur--) << 8; + uint16_t on_top = (color & 0x8000) ^ pri; + if (on_top) { + *dst = map_32x_color(color); + } else { + uint32_t h32_pos = (dst - output) * 0x40000 / 5; + uint32_t base_index = h32_pos >> 16; + h32_pos &= 0xFFFF; + if (h32_pos > 0xE000) { + base_index++; + } else if (h32_pos >= 0x1000) { + uint8_t first_opaque = compositebuf[base_index] & 0xF; + uint8_t second_opaque = compositebuf[base_index+1] & 0xF; + if (first_opaque && second_opaque) { + *dst = mix_colors(output[base_index], output[base_index + 1], h32_pos); + } else if (first_opaque) { + *dst = mix_colors(output[base_index], map_32x_color(color), h32_pos); + } else if (second_opaque) { + *dst = mix_colors(map_32x_color(color), output[base_index + 1], h32_pos); + } else { + *dst = map_32x_color(color); + } + continue; + } + if (compositebuf[base_index] & 0xF) { + *dst = output[base_index]; + } else { + *dst = map_32x_color(color); + } + } + } +} + +static void s32x_composite_direct(s32x_video *vid, pixel_t *output, uint8_t *compositebuf, uint32_t line_start, uint8_t is_h40) +{ + if (!is_h40) { + s32x_composite_direct_h32(vid, output, compositebuf, line_start); + return; + } + //does shift do anything in this mode? + uint8_t *cur = vid->front + line_start; + uint16_t pri = (vid->regs[S32X_VID_MODE] & S32X_VID_BIT_PRI) << 8; + for (pixel_t *end = output + 320; output < end; output++) + { + uint16_t color = *cur << 8 | cur[1]; + cur += 2; + uint16_t on_top = (color & 0x8000) ^ pri; + if (on_top || !(*compositebuf & 0xF)) { + *output = map_32x_color(color); + } + compositebuf++; + } +} + +static void s32x_composite_rle_h32(s32x_video *vid, pixel_t *output, uint8_t *compositebuf, uint32_t line_start) +{ + pixel_t md_layer[257]; + memcpy(md_layer, output, sizeof(md_layer)); + uint8_t *cur = vid->front + line_start; + uint16_t pri = (vid->regs[S32X_VID_MODE] & S32X_VID_BIT_PRI) << 8; + uint32_t count = 0; + uint16_t color; + pixel_t *start = output; + for (pixel_t *end = output + 320; output < end; output++) + { + if (!count) { + count = *(cur++) + 1; + color = vid->palette[*(cur++)]; + } + count--; + uint16_t on_top = (color & 0x8000) ^ pri; + if (on_top) { + *output = map_32x_color(color); + } else { + uint32_t h32_pos = (output - start) * 0x40000 / 5; + uint32_t base_index = h32_pos >> 16; + h32_pos &= 0xFFFF; + if (h32_pos > 0xE000) { + base_index++; + } else if (h32_pos >= 0x1000) { + //mix + uint8_t first_opaque = compositebuf[base_index] & 0xF; + uint8_t second_opaque = compositebuf[base_index+1] & 0xF; + if (first_opaque && second_opaque) { + *output = mix_colors(md_layer[base_index], md_layer[base_index + 1], h32_pos); + } else if (first_opaque) { + *output = mix_colors(md_layer[base_index], map_32x_color(color), h32_pos); + } else if (second_opaque) { + *output = mix_colors(map_32x_color(color), md_layer[base_index + 1], h32_pos); + } else { + *output = map_32x_color(color); + } + continue; + } + if (compositebuf[base_index] & 0xF) { + *output = output[base_index]; + } else { + *output = map_32x_color(color); + } + } + } +} + +static void s32x_composite_rle(s32x_video *vid, pixel_t *output, uint8_t *compositebuf, uint32_t line_start, uint8_t is_h40) +{ + if (!is_h40) { + s32x_composite_rle_h32(vid, output, compositebuf, line_start); + return; + } + //does shift do anything in this mode? + uint8_t *cur = vid->front + line_start; + uint16_t pri = (vid->regs[S32X_VID_MODE] & S32X_VID_BIT_PRI) << 8; + uint32_t count = 0; + uint16_t color; + for (pixel_t *end = output + 320; output < end; output++) + { + if (!count) { + count = *(cur++) + 1; + color = vid->palette[*(cur++)]; + } + uint16_t on_top = (color & 0x8000) ^ pri; + if (on_top || !(*compositebuf & 0xF)) { + *output = map_32x_color(color); + } + compositebuf++; + count--; + } +} + +uint8_t s32x_video_composite(s32x_video *vid, pixel_t *output, uint8_t *compositebuf, uint32_t line, uint8_t is_h40) +{ + uint32_t line_start = vid->front[line * 2] << 9 | vid->front[line * 2 + 1] << 1; + switch (vid->regs[S32X_VID_MODE] & S32X_VID_MODE_MASK) + { + case S32X_VID_MODE_BLANK: + return 0; + case S32X_VID_MODE_INDEXED: + s32x_composite_indexed(vid, output, compositebuf, line_start, is_h40); + break; + case S32X_VID_MODE_DIRECT: + s32x_composite_direct(vid, output, compositebuf, line_start, is_h40); + break; + case S32X_VID_MODE_RLE: + s32x_composite_rle(vid, output, compositebuf, line_start, is_h40); + break; + } + return 1; +} + +uint16_t s32x_video_68k_read(uint32_t address, s32x_video *video) +{ + //TODO: check FM + if (address < 0xA15180 + S32X_NUM_VID_REGS * 2) { + uint32_t reg = (address & 0xF) >> 1; + if (reg == S32X_VID_FB_CTRL && video->hcounter >= HBLANK_START && video->hcounter < HBLANK_START + 6) { + //TODO: determine all times VRAM refresh happens + //refresh + return video->regs[reg] | S32X_VID_BIT_FEN; + } + return video->regs[reg]; + } else if (address >= 0xA15200 && address < 0xA15400) { + return video->palette[(address & 0x1FF) >> 1]; + } + return 0xFFFF; +} + +uint16_t s32x_video_sh2_read(uint32_t address, s32x_video *video) +{ + //TODO: check FM + if (address < 0x0004100 + S32X_NUM_VID_REGS * 2) { + uint32_t reg = (address & 0xF) >> 1; + if (reg == S32X_VID_FB_CTRL && video->hcounter >= HBLANK_START && video->hcounter < HBLANK_START + 6) { + //TODO: determine all times VRAM refresh happens + //refresh + return video->regs[reg] | S32X_VID_BIT_FEN; + } + return video->regs[reg]; + } else if (address >= 0x0004200 && address < 0x0004400) { + return video->palette[(address & 0x1FF) >> 1]; + } + return 0xFFFF; +} + +uint32_t s32x_cycles_to_vblank(s32x_video *video) +{ + uint32_t cycles; + uint16_t vblank_start = 224, frame_end; + if (!(video->regs[S32X_VID_MODE] & S32X_VID_BIT_PAL)) { + frame_end = 313; + if (video->regs[S32X_VID_MODE] & S32X_VID_BIT_V240) { + vblank_start = 240; + } + } else { + frame_end = 262; + } + if (video->vcounter < vblank_start) { + cycles = (vblank_start - 1 - video->vcounter) * MCLKS_LINE; + } else { + cycles = (vblank_start - 1 + frame_end - video->vcounter) * MCLKS_LINE; + } + if (video->hcounter <= HSYNC_START) { + cycles += 3420 - video->hcounter * MCLKS_PIXEL; + } else if (video->hcounter >= HSYNC_END) { + cycles += (LINE_END - video->hcounter) * MCLKS_PIXEL; + } else { + cycles += (LINE_END - HSYNC_END) * MCLKS_PIXEL; + for (uint16_t i = video->hcounter; i < HSYNC_END; i++) + { + cycles += mclks_pixel[i - HSYNC_START]; + } + } + return cycles; +} + +static uint32_t cycles_to_pen(s32x_video *video) +{ + return (HBLANK_START - video->hcounter) * MCLKS_PIXEL; +} + +uint32_t s32x_cycles_to_hint(s32x_video *video) +{ + uint16_t vblank_start = 224, frame_end; + if (!(video->regs[S32X_VID_MODE] & S32X_VID_BIT_PAL)) { + frame_end = 313; + if (video->regs[S32X_VID_MODE] & S32X_VID_BIT_V240) { + vblank_start = 240; + } + } else { + frame_end = 262; + } + uint16_t next_hint_line = video->vcounter + video->hint_counter; + if (video->hcounter > HBLANK_START) { + next_hint_line++; + } + uint32_t cycles; + if ((next_hint_line < vblank_start - 1) || video->hen) { + cycles = video->hint_counter * MCLKS_LINE; + } else { + cycles = (video->hint_count + (frame_end - 2 - video->vcounter)) * MCLKS_LINE; + if (video->hcounter < HBLANK_START) { + cycles += MCLKS_LINE; + } + } + if (video->hcounter <= HBLANK_START) { + //just add cycles left to HBLANK_START + return cycles + (HBLANK_START - video->hcounter) * MCLKS_PIXEL; + } else if (video->hcounter <= HSYNC_START) { + //add cycles until end of line + cycles to HBLANK_START on the next line + return cycles + HBLANK_START * MCLKS_PIXEL + MCLKS_LINE - video->hcounter * MCLKS_PIXEL; + } else if (video->hcounter >= HSYNC_END) { + //same as above, just calculated differently + return cycles + (HBLANK_START + LINE_END - video->hcounter) * MCLKS_PIXEL; + } else { + //same as above, but annoying + for (uint16_t hcnt = video->hcounter; hcnt < HSYNC_END; hcnt++) + { + cycles += mclks_pixel[hcnt - HSYNC_START]; + } + return cycles + (HBLANK_START + LINE_END - HSYNC_END) * MCLKS_PIXEL; + } +} + +static uint16_t video_write_mask[] = { + 0x00C3, + 0x0001, + 0x00FF, + 0xFFFF, + 0xFFFF, + 0x0001, +}; +uint32_t s32x_video_68k_write(uint32_t address, s32x_video *video, uint16_t value) +{ + if (address < 0xA15180 + S32X_NUM_VID_REGS * 2) { + uint32_t reg = (address & 0xF) >> 1; + uint16_t mask = video_write_mask[reg]; + uint16_t old = video->regs[reg]; + uint16_t new = (old & ~mask) | (value & mask); + uint16_t changed = old ^ new; + if (reg == S32X_VID_FB_CTRL && (changed & S32X_VID_BIT_FS)) { + if (old & S32X_VID_BIT_VBLK || !(video->regs[S32X_VID_MODE] & S32X_VID_MODE_MASK)) { + uint8_t *tmp = video->front; + video->front = video->back; + video->back = tmp; + video->flip_pending = 0; + } else { + video->flip_pending = 1; + new &= ~S32X_VID_BIT_FS; + new |= old & S32X_VID_BIT_FS; + } + } + dprintf("32X VDP Write: %06X: %04X\n", address, value); + video->regs[reg] = new; + if (reg == S32X_VID_FILL_DATA) { + video->fill_count = video->regs[S32X_VID_FILL_LEN]; + video->regs[S32X_VID_FB_CTRL] |= S32X_VID_BIT_FEN; + video->fill_remainder = 0; + } + } else if (address >= 0xA15200 && address < 0xA15400) { + if (!(video->regs[S32X_VID_FB_CTRL] & S32X_VID_BIT_PEN)) { + return cycles_to_pen(video); + } + dprintf("32X Palette Write: %06X: %04X\n", address, value); + video->palette[(address & 0x1FF) >> 1] = value; + } + return 0; +} + +uint32_t s32x_video_68k_write_b(uint32_t address, s32x_video *video, uint16_t value) +{ + if (address < 0xA15180 + S32X_NUM_VID_REGS * 2) { + uint32_t reg = (address & 0xF) >> 1; + uint16_t mask = video_write_mask[reg]; + uint16_t extended; + if (address & 1) { + extended = value; + mask &= 0x00FF;; + } else { + extended = value << 8; + mask &= 0xFF00; + } + uint16_t old = video->regs[reg]; + uint16_t new = (old & ~mask) | (extended & mask); + uint16_t changed = old ^ new; + if (reg == S32X_VID_FB_CTRL && (changed & S32X_VID_BIT_FS)) { + if (old & S32X_VID_BIT_VBLK || !(video->regs[S32X_VID_MODE] & S32X_VID_MODE_MASK)) { + uint8_t *tmp = video->front; + video->front = video->back; + video->back = tmp; + video->flip_pending = 0; + } else { + video->flip_pending = 1; + new &= ~S32X_VID_BIT_FS; + new |= old & S32X_VID_BIT_FS; + } + } + dprintf("32X VDP Write (byte): %06X: %04X\n", address, value); + video->regs[reg] = new; + if (reg == S32X_VID_FILL_DATA) { + video->fill_count = video->regs[S32X_VID_FILL_LEN]; + video->regs[S32X_VID_FB_CTRL] |= S32X_VID_BIT_FEN; + } + } else if (address >= 0xA15200 && address < 0xA15400) { + if (!(video->regs[S32X_VID_FB_CTRL] & S32X_VID_BIT_PEN)) { + return cycles_to_pen(video); + } + dprintf("32X Palette Write (byte): %06X: %04X\n", address, value); + //manual says this isn't allowed, what actually happens here? + } + return 0; +} + +uint32_t s32x_video_sh2_write(uint32_t address, s32x_video *video, uint16_t value) +{ + if (address < 0x0004100 + S32X_NUM_VID_REGS * 2) { + uint32_t reg = (address & 0xF) >> 1; + uint16_t mask = video_write_mask[reg]; + uint16_t old = video->regs[reg]; + uint16_t new = (old & ~mask) | (value & mask); + uint16_t changed = old ^ new; + if (reg == S32X_VID_FB_CTRL && (changed & S32X_VID_BIT_FS)) { + if (old & S32X_VID_BIT_VBLK || !(video->regs[S32X_VID_MODE] & S32X_VID_MODE_MASK)) { + uint8_t *tmp = video->front; + video->front = video->back; + video->back = tmp; + video->flip_pending = 0; + } else { + video->flip_pending = 1; + new &= ~S32X_VID_BIT_FS; + new |= old & S32X_VID_BIT_FS; + } + } + dprintf("32X VDP Write: %06X: %04X\n", address, value); + video->regs[reg] = new; + if (reg == S32X_VID_FILL_DATA) { + video->fill_count = video->regs[S32X_VID_FILL_LEN]; + video->regs[S32X_VID_FB_CTRL] |= S32X_VID_BIT_FEN; + } + } else if (address >= 0x0004200 && address < 0x0004400) { + if (!(video->regs[S32X_VID_FB_CTRL] & S32X_VID_BIT_PEN)) { + return cycles_to_pen(video) * 3; + } + dprintf("32X Palette Write: %06X: %04X\n", address, value); + video->palette[(address & 0x1FF) >> 1] = value; + } + return 0; +} + +uint32_t s32x_video_sh2_write_b(uint32_t address, s32x_video *video, uint8_t value) +{ + if (address < 0x0004100 + S32X_NUM_VID_REGS * 2) { + uint32_t reg = (address & 0xF) >> 1; + uint16_t mask = video_write_mask[reg]; + uint16_t extended; + if (address & 1) { + extended = value; + mask &= 0x00FF;; + } else { + extended = value << 8; + mask &= 0xFF00; + } + uint16_t old = video->regs[reg]; + uint16_t new = (old & ~mask) | (extended & mask); + uint16_t changed = old ^ new; + if (reg == S32X_VID_FB_CTRL && (changed & S32X_VID_BIT_FS)) { + if (old & S32X_VID_BIT_VBLK || !(video->regs[S32X_VID_MODE] & S32X_VID_MODE_MASK)) { + uint8_t *tmp = video->front; + video->front = video->back; + video->back = tmp; + video->flip_pending = 0; + } else { + video->flip_pending = 1; + new &= ~S32X_VID_BIT_FS; + new |= old & S32X_VID_BIT_FS; + } + } + dprintf("32X VDP Write (byte): %06X: %04X\n", address, value); + video->regs[reg] = new; + if (reg == S32X_VID_FILL_DATA) { + video->fill_count = video->regs[S32X_VID_FILL_LEN]; + video->regs[S32X_VID_FB_CTRL] |= S32X_VID_BIT_FEN; + } + } else if (address >= 0x0004200 && address < 0x0004400) { + if (!(video->regs[S32X_VID_FB_CTRL] & S32X_VID_BIT_PEN)) { + return cycles_to_pen(video) * 3; + } + dprintf("32X Palette Write (byte): %06X: %04X\n", address, value); + //manual says this isn't allowed, what actually happens here? + } + return 0; +} + +void s32x_video_fb_write_w(uint32_t address, s32x_video *video, uint16_t value) +{ + address &= 0x1FFFE; + video->back[address] = value >> 8; + video->back[address | 1] = value; +} + +void s32x_video_fb_write_b(uint32_t address, s32x_video *video, uint8_t value) +{ + address &= 0x1FFFF; + video->back[address] = value; +} + +uint16_t s32x_video_fb_read_w(uint32_t address, s32x_video *video) +{ + address &= 0x1FFFE; + return video->back[address] << 8 | video->back[address | 1]; +} + +uint16_t s32x_video_fb_read_b(uint32_t address, s32x_video *video) +{ + address &= 0x1FFFF; + return video->back[address]; +} + +void s32x_video_overwrite_write_w(uint32_t address, s32x_video *video, uint16_t value) +{ + address &= 0x1FFFE; + uint8_t first = value >> 8; + uint8_t second = value; + if (first) { + video->back[address] = first; + } + if (second) { + video->back[address | 1] = second; + } +} + +void s32x_video_overwrite_write_b(uint32_t address, s32x_video *video, uint8_t value) +{ + address &= 0x1FFFF; + if (value) { + video->back[address] = value; + } +} + +static void s32x_fb_debug_indexed(pixel_t *fb, uint32_t pitch, s32x_video *video, uint8_t *buffer, uint16_t vblank_start, pixel_t *colors, uint32_t *line_offsets) +{ + //TODO: display offscreen portions with a scroll rect like for MD VDP planes + for (int y = 0; y < vblank_start; y++) + { + pixel_t *cur = fb; + fb += pitch / sizeof(pixel_t); + uint32_t cur_off = line_offsets[y]; + if (video->regs[S32X_VID_SHIFT] & 1) { + cur_off += 1; + cur_off &= 0x1FFFF; + } + for (int x = 0; x < 320; x++) + { + *(cur++) = colors[buffer[cur_off++]]; + cur_off &= 0x1FFFF; + } + } +} + +static void s32x_fb_debug_rle(pixel_t *fb, uint32_t pitch, s32x_video *video, uint8_t *buffer, uint16_t vblank_start, pixel_t *colors, uint32_t *line_offsets) +{ + //TODO: implement me +} + +static void s32x_fb_debug_direct(pixel_t *fb, uint32_t pitch, s32x_video *video, uint8_t *buffer, uint16_t vblank_start, uint32_t *line_offsets) +{ + //TODO: display offscreen portions with a scroll rect like for MD VDP planes + //TODO: display offscreen portions with a scroll rect like for MD VDP planes + for (int y = 0; y < vblank_start; y++) + { + pixel_t *cur = fb; + fb += pitch / sizeof(pixel_t); + uint32_t cur_off = line_offsets[y]; + for (int x = 0; x < 320; x++) + { + uint16_t color = buffer[cur_off++] << 8; + cur_off &= 0x1FFFF; + color |= buffer[cur_off++]; + *(cur++) = render_map_color(color << 3 & 0xF8, color >> 2 & 0xF8, color >> 7 & 0xF8); + cur_off &= 0x1FFFF; + } + } +} + +static void s32x_fb_debug_one(pixel_t *fb, uint32_t pitch, s32x_video *video, uint8_t *buffer) +{ + pixel_t colors[256]; + pixel_t black = render_map_color(0, 0, 0); + uint32_t line_offsets[240]; + int vblank_start; + if (!(video->regs[S32X_VID_MODE] & S32X_VID_BIT_PAL) && (video->regs[S32X_VID_MODE] & S32X_VID_BIT_V240)) { + vblank_start = 240; + } else { + vblank_start = 224; + } + for (int i = 0; i < vblank_start; i++) + { + line_offsets[i] = buffer[i << 1] << 9 | buffer[i << 1 | 1] << 1; + } + switch (video->regs[S32X_VID_MODE] & S32X_VID_MODE_MASK) + { + case S32X_VID_MODE_BLANK: + break; + case S32X_VID_MODE_INDEXED: + case S32X_VID_MODE_RLE: + for (int i = 0; i < 256; i++) + { + uint16_t color = video->palette[i]; + colors[i] = render_map_color(color << 3 & 0xF8, color >> 2 & 0xF8, color >> 7 & 0xF8); + } + if ((video->regs[S32X_VID_MODE] & S32X_VID_MODE_MASK) == S32X_VID_MODE_INDEXED) { + s32x_fb_debug_indexed(fb, pitch, video, buffer, vblank_start, colors, line_offsets); + } else { + s32x_fb_debug_rle(fb, pitch, video, buffer, vblank_start, colors, line_offsets); + } + break; + case S32X_VID_MODE_DIRECT: + s32x_fb_debug_direct(fb, pitch, video, buffer, vblank_start, line_offsets); + break; + } +} + +void s32x_fb_debug(pixel_t *fb, uint32_t pitch, s32x_video *video) +{ + s32x_fb_debug_one(fb, pitch, video, video->front); + s32x_fb_debug_one(fb + pitch*512/sizeof(pixel_t), pitch, video, video->back); +} diff --git a/32x_video.h b/32x_video.h new file mode 100644 index 0000000..31fc0fc --- /dev/null +++ b/32x_video.h @@ -0,0 +1,75 @@ +#ifndef S32X_VIDEO_H_ +#define S32X_VIDEO_H_ +#include +#include "pixel.h" + +enum { + S32X_VID_MODE, + S32X_VID_SHIFT, + S32X_VID_FILL_LEN, + S32X_VID_FILL_START, + S32X_VID_FILL_DATA, + S32X_VID_FB_CTRL, + S32X_NUM_VID_REGS +}; + +#define S32X_VID_BIT_PAL 0x8000 +#define S32X_VID_BIT_V240 0x0040 +#define S32X_VID_BIT_PRI 0x0080 +#define S32X_VID_MODE_MASK 0x0003 + +#define S32X_VID_BIT_VBLK 0x8000 +#define S32X_VID_BIT_HBLK 0x4000 +#define S32X_VID_BIT_PEN 0x2000 +#define S32X_VID_BIT_FEN 0x0002 +#define S32X_VID_BIT_FS 0x0001 + +enum { + S32X_VID_MODE_BLANK, + S32X_VID_MODE_INDEXED, + S32X_VID_MODE_DIRECT, + S32X_VID_MODE_RLE +}; + +typedef struct { + uint8_t *front; + uint8_t *back; + uint32_t cycle; + uint32_t fill_remainder; + uint16_t vcounter; + uint16_t hcounter; + uint16_t regs[S32X_NUM_VID_REGS]; + uint16_t palette[256]; + uint16_t fill_count; + uint16_t hint_count; + uint16_t hint_counter; + //TODO: FIFO + uint8_t main_vint_pending; + uint8_t sub_vint_pending; + uint8_t main_hint_pending; + uint8_t sub_hint_pending; + uint8_t hen; + uint8_t flip_pending; +} s32x_video; + +void s32x_video_init(s32x_video *vid, uint8_t pal); +void s32x_video_free(s32x_video *vid); +void s32x_video_run(s32x_video *vid, uint32_t target); +uint8_t s32x_video_composite(s32x_video *vid, pixel_t *output, uint8_t *compositebuf, uint32_t line, uint8_t is_h40); +uint32_t s32x_cycles_to_vblank(s32x_video *video); +uint32_t s32x_cycles_to_hint(s32x_video *video); +uint16_t s32x_video_68k_read(uint32_t address, s32x_video *video); +uint16_t s32x_video_sh2_read(uint32_t address, s32x_video *video); +uint32_t s32x_video_68k_write(uint32_t address, s32x_video *video, uint16_t value); +uint32_t s32x_video_68k_write_b(uint32_t address, s32x_video *video, uint16_t value); +uint32_t s32x_video_sh2_write(uint32_t address, s32x_video *video, uint16_t value); +uint32_t s32x_video_sh2_write_b(uint32_t address, s32x_video *video, uint8_t value); +void s32x_video_fb_write_w(uint32_t address, s32x_video *video, uint16_t value); +void s32x_video_fb_write_b(uint32_t address, s32x_video *video, uint8_t value); +uint16_t s32x_video_fb_read_w(uint32_t address, s32x_video *video); +uint16_t s32x_video_fb_read_b(uint32_t address, s32x_video *video); +void s32x_video_overwrite_write_w(uint32_t address, s32x_video *video, uint16_t value); +void s32x_video_overwrite_write_b(uint32_t address, s32x_video *video, uint8_t value); +void s32x_fb_debug(pixel_t *fb, uint32_t pitch, s32x_video *video); + +#endif //S32X_VIDEO_H_ diff --git a/68kinst.c b/68kinst.c index d20c1a3..99bb85a 100644 --- a/68kinst.c +++ b/68kinst.c @@ -4,6 +4,7 @@ BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #include "68kinst.h" +#include "disasm.h" #include #include @@ -17,7 +18,9 @@ uint32_t sign_extend8(uint32_t val) return (val & 0x80) ? val | 0xFFFFFF00 : val; } -uint16_t *m68k_decode_op_ex(uint16_t *cur, uint8_t mode, uint8_t reg, uint8_t size, m68k_op_info *dst) +#define INVALID_ADDRESS 0xFFFFFFFF + +uint32_t m68k_decode_op_ex(uint16_t opcode, uint32_t address, m68k_fetch_fun fetch, void *data, uint8_t mode, uint8_t reg, uint8_t size, m68k_op_info *dst) { uint16_t ext, tmp; dst->addr_mode = mode; @@ -31,13 +34,15 @@ uint16_t *m68k_decode_op_ex(uint16_t *cur, uint8_t mode, uint8_t reg, uint8_t si dst->params.regs.pri = reg; break; case MODE_AREG_DISPLACE: - ext = *(++cur); + ext = fetch(address, data); + address += 2; dst->params.regs.pri = reg; dst->params.regs.displacement = sign_extend16(ext); break; case MODE_AREG_INDEX_MEM: dst->params.regs.pri = reg; - ext = *(++cur); + ext = fetch(address, data); + address += 2; dst->params.regs.sec = ext >> 11;//includes areg/dreg bit, reg num and word/long bit #ifdef M68020 dst->params.regs.scale = ext >> 9 & 3; @@ -48,16 +53,19 @@ uint16_t *m68k_decode_op_ex(uint16_t *cur, uint8_t mode, uint8_t reg, uint8_t si { case 0: //reserved - return NULL; + return INVALID_ADDRESS; case 1: dst->params.regs.displacement = 0; break; case 2: - dst->params.regs.displacement = sign_extend16(*(cur++)); + dst->params.regs.displacement = sign_extend16(fetch(address, data)); + address += 2; break; case 3: - tmp = *(cur++); - dst->params.regs.displacement = tmp << 16 | *(cur++); + tmp = fetch(address, data); + address += 2; + dst->params.regs.displacement = tmp << 16 | fetch(address, data); + address += 2; break; } if (ext & 0x3) @@ -89,16 +97,19 @@ uint16_t *m68k_decode_op_ex(uint16_t *cur, uint8_t mode, uint8_t reg, uint8_t si { case 0: //reserved - return NULL; + return INVALID_ADDRESS; case 1: dst->params.regs.outer_disp = 0; break; case 2: - dst->params.regs.outer_disp = sign_extend16(*(cur++)); + dst->params.regs.outer_disp = sign_extend16(fetch(address, data)); + address += 2; break; case 3: - tmp = *(cur++); - dst->params.regs.outer_disp = tmp << 16 | *(cur++); + tmp = fetch(address, data); + address += 2; + dst->params.regs.outer_disp = tmp << 16 | fetch(address, data); + address += 2; break; } } else { @@ -131,16 +142,20 @@ uint16_t *m68k_decode_op_ex(uint16_t *cur, uint8_t mode, uint8_t reg, uint8_t si { case 0: dst->addr_mode = MODE_ABSOLUTE_SHORT; - ext = *(++cur); + ext = fetch(address, data); + address += 2; dst->params.immed = sign_extend16(ext); break; case 1: dst->addr_mode = MODE_ABSOLUTE; - ext = *(++cur); - dst->params.immed = ext << 16 | *(++cur); + ext = fetch(address, data); + address += 2; + dst->params.immed = ((uint32_t)ext) << 16 | fetch(address, data); + address += 2; break; case 3: - ext = *(++cur); + ext = fetch(address, data); + address += 2; dst->params.regs.sec = ext >> 11;//includes areg/dreg bit, reg num and word/long bit #ifdef M68020 dst->params.regs.scale = ext >> 9 & 3; @@ -151,16 +166,18 @@ uint16_t *m68k_decode_op_ex(uint16_t *cur, uint8_t mode, uint8_t reg, uint8_t si { case 0: //reserved - return NULL; + return INVALID_ADDRESS; case 1: dst->params.regs.displacement = 0; break; case 2: - dst->params.regs.displacement = sign_extend16(*(cur++)); + dst->params.regs.displacement = sign_extend16(fetch(address, data)); + address += 2; break; case 3: tmp = *(cur++); - dst->params.regs.displacement = tmp << 16 | *(cur++); + dst->params.regs.displacement = tmp << 16 | fetch(address, data); + address += 2; break; } if (ext & 0x3) @@ -192,16 +209,19 @@ uint16_t *m68k_decode_op_ex(uint16_t *cur, uint8_t mode, uint8_t reg, uint8_t si { case 0: //reserved - return NULL; + return INVALID_ADDRESS; case 1: dst->params.regs.outer_disp = 0; break; case 2: - dst->params.regs.outer_disp = sign_extend16(*(cur++)); + dst->params.regs.outer_disp = sign_extend16(fetch(address, data)); + address += 2; break; case 3: - tmp = *(cur++); - dst->params.regs.outer_disp = tmp << 16 | *(cur++); + tmp = fetch(address, data); + address += 2; + dst->params.regs.outer_disp = tmp << 16 | fetch(address, data); + address += 2; break; } } else { @@ -231,12 +251,14 @@ uint16_t *m68k_decode_op_ex(uint16_t *cur, uint8_t mode, uint8_t reg, uint8_t si break; case 2: dst->addr_mode = MODE_PC_DISPLACE; - ext = *(++cur); + ext = fetch(address, data); + address += 2; dst->params.regs.displacement = sign_extend16(ext); break; case 4: dst->addr_mode = MODE_IMMEDIATE; - ext = *(++cur); + ext = fetch(address, data); + address += 2; switch (size) { case OPSIZE_BYTE: @@ -246,16 +268,17 @@ uint16_t *m68k_decode_op_ex(uint16_t *cur, uint8_t mode, uint8_t reg, uint8_t si dst->params.immed = ext; break; case OPSIZE_LONG: - dst->params.immed = ext << 16 | *(++cur); + dst->params.immed = ((uint32_t)ext) << 16 | fetch(address, data); + address += 2; break; } break; default: - return NULL; + return INVALID_ADDRESS; } break; } - return cur; + return address; } uint8_t m68k_valid_immed_dst(m68k_op_info *dst) @@ -290,11 +313,11 @@ uint8_t m68k_valid_movem_dst(m68k_op_info *dst) return m68k_valid_immed_limited_dst(dst); } -uint16_t *m68k_decode_op(uint16_t *cur, uint8_t size, m68k_op_info *dst) +uint32_t m68k_decode_op(uint16_t opcode, uint32_t address, m68k_fetch_fun fetch, void *data, uint8_t size, m68k_op_info *dst) { - uint8_t mode = (*cur >> 3) & 0x7; - uint8_t reg = *cur & 0x7; - return m68k_decode_op_ex(cur, mode, reg, size, dst); + uint8_t mode = (opcode >> 3) & 0x7; + uint8_t reg = opcode & 0x7; + return m68k_decode_op_ex(opcode, address, fetch, data, mode, reg, size, dst); } void m68k_decode_cond(uint16_t op, m68kinst * decoded) @@ -307,10 +330,12 @@ uint8_t m68k_reg_quick_field(uint16_t op) return (op >> 9) & 0x7; } -uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) +uint32_t m68k_decode(m68k_fetch_fun fetch, void *data, m68kinst * decoded, uint32_t address) { - uint16_t *start = istream; - uint8_t optype = *istream >> 12; + uint32_t start_address = address; + uint16_t opcode = fetch(address, data); + address += 2; + uint8_t optype = opcode >> 12; uint8_t size; uint8_t reg; uint8_t opmode; @@ -318,32 +343,34 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->op = M68K_INVALID; decoded->src.addr_mode = decoded->dst.addr_mode = MODE_UNUSED; decoded->variant = VAR_NORMAL; - decoded->address = address; + decoded->address = start_address; switch(optype) { case BIT_MOVEP_IMMED: - if ((*istream & 0x138) == 0x108) { + if ((opcode & 0x138) == 0x108) { //MOVEP decoded->op = M68K_MOVEP; - decoded->extra.size = *istream & 0x40 ? OPSIZE_LONG : OPSIZE_WORD; - if (*istream & 0x80) { + decoded->extra.size = opcode & 0x40 ? OPSIZE_LONG : OPSIZE_WORD; + if (opcode & 0x80) { //memory dest decoded->src.addr_mode = MODE_REG; - decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); + decoded->src.params.regs.pri = m68k_reg_quick_field(opcode); decoded->dst.addr_mode = MODE_AREG_DISPLACE; - decoded->dst.params.regs.pri = *istream & 0x7; - decoded->dst.params.regs.displacement = *(++istream); + decoded->dst.params.regs.pri = opcode & 0x7; + decoded->dst.params.regs.displacement = fetch(address, data); + address += 2; } else { //memory source decoded->dst.addr_mode = MODE_REG; - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); decoded->src.addr_mode = MODE_AREG_DISPLACE; - decoded->src.params.regs.pri = *istream & 0x7; - decoded->src.params.regs.displacement = *(++istream); + decoded->src.params.regs.pri = opcode & 0x7; + decoded->src.params.regs.displacement = fetch(address, data); + address += 2; } - } else if (*istream & 0x100) { + } else if (opcode & 0x100) { //BTST, BCHG, BCLR, BSET - switch ((*istream >> 6) & 0x3) + switch ((opcode >> 6) & 0x3) { case 0: decoded->op = M68K_BTST; @@ -359,11 +386,11 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) break; } decoded->src.addr_mode = MODE_REG; - decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); + decoded->src.params.regs.pri = m68k_reg_quick_field(opcode); decoded->extra.size = OPSIZE_BYTE; - istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->dst)); + address = m68k_decode_op(opcode, address, fetch, data, decoded->extra.size, &(decoded->dst)); if ( - !istream || decoded->dst.addr_mode == MODE_AREG + address == INVALID_ADDRESS || decoded->dst.addr_mode == MODE_AREG || (decoded->op != M68K_BTST && !m68k_valid_immed_limited_dst(&decoded->dst)) ) { decoded->op = M68K_INVALID; @@ -372,9 +399,9 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) if (decoded->dst.addr_mode == MODE_REG) { decoded->extra.size = OPSIZE_LONG; } - } else if ((*istream & 0xF00) == 0x800) { + } else if ((opcode & 0xF00) == 0x800) { //BTST, BCHG, BCLR, BSET - switch ((*istream >> 6) & 0x3) + switch ((opcode >> 6) & 0x3) { case 0: decoded->op = M68K_BTST; @@ -389,14 +416,15 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->op = M68K_BSET; break; } - opmode = (*istream >> 3) & 0x7; - reg = *istream & 0x7; + opmode = (opcode >> 3) & 0x7; + reg = opcode & 0x7; decoded->src.addr_mode = MODE_IMMEDIATE_WORD; - decoded->src.params.immed = *(++istream) & 0xFF; + decoded->src.params.immed = fetch(address, data) & 0xFF; + address += 2; decoded->extra.size = OPSIZE_BYTE; - istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst)); + address = m68k_decode_op_ex(opcode, address, fetch, data, opmode, reg, decoded->extra.size, &(decoded->dst)); if ( - !istream || !m68k_valid_immed_dst(&decoded->dst) + address == INVALID_ADDRESS || !m68k_valid_immed_dst(&decoded->dst) || (decoded->op != M68K_BTST && !m68k_valid_immed_limited_dst(&decoded->dst)) ) { decoded->op = M68K_INVALID; @@ -405,46 +433,52 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) if (decoded->dst.addr_mode == MODE_REG) { decoded->extra.size = OPSIZE_LONG; } - } else if ((*istream & 0xC0) == 0xC0) { + } else if ((opcode & 0xC0) == 0xC0) { #ifdef M68020 //CMP2, CHK2, CAS, CAS2, RTM, CALLM #endif } else { - switch ((*istream >> 9) & 0x7) + switch ((opcode >> 9) & 0x7) { case 0: - if ((*istream & 0xFF) == 0x3C) { + if ((opcode & 0xFF) == 0x3C) { decoded->op = M68K_ORI_CCR; decoded->extra.size = OPSIZE_BYTE; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->src.params.immed = *(++istream) & 0xFF; - } else if((*istream & 0xFF) == 0x7C) { + decoded->src.params.immed = fetch(address, data) & 0xFF; + address += 2; + } else if((opcode & 0xFF) == 0x7C) { decoded->op = M68K_ORI_SR; decoded->extra.size = OPSIZE_WORD; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->src.params.immed = *(++istream); + decoded->src.params.immed = fetch(address, data); + address += 2; } else { decoded->op = M68K_OR; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->extra.size = size = (*istream >> 6) & 3; - reg = *istream & 0x7; - opmode = (*istream >> 3) & 0x7; + decoded->extra.size = size = (opcode >> 6) & 3; + reg = opcode & 0x7; + opmode = (opcode >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: - decoded->src.params.immed = *(++istream) & 0xFF; + decoded->src.params.immed = fetch(address, data) & 0xFF; + address += 2; break; case OPSIZE_WORD: - decoded->src.params.immed = *(++istream); + decoded->src.params.immed = fetch(address, data); + address += 2; break; case OPSIZE_LONG: - immed = *(++istream); - decoded->src.params.immed = immed << 16 | *(++istream); + immed = fetch(address, data); + address += 2; + decoded->src.params.immed = immed << 16 | fetch(address, data); + address += 2; break; } - istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); - if (!istream || !m68k_valid_immed_limited_dst(&(decoded->dst))) { + address = m68k_decode_op_ex(opcode, address, fetch, data, opmode, reg, size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_immed_limited_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } @@ -452,38 +486,44 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) break; case 1: //ANDI, ANDI to CCR, ANDI to SR - if ((*istream & 0xFF) == 0x3C) { + if ((opcode & 0xFF) == 0x3C) { decoded->op = M68K_ANDI_CCR; decoded->extra.size = OPSIZE_BYTE; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->src.params.immed = *(++istream) & 0xFF; - } else if((*istream & 0xFF) == 0x7C) { + decoded->src.params.immed = fetch(address, data) & 0xFF; + address += 2; + } else if((opcode & 0xFF) == 0x7C) { decoded->op = M68K_ANDI_SR; decoded->extra.size = OPSIZE_WORD; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->src.params.immed = *(++istream); + decoded->src.params.immed = fetch(address, data); + address += 2; } else { decoded->op = M68K_AND; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->extra.size = size = (*istream >> 6) & 3; - reg = *istream & 0x7; - opmode = (*istream >> 3) & 0x7; + decoded->extra.size = size = (opcode >> 6) & 3; + reg = opcode & 0x7; + opmode = (opcode >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: - decoded->src.params.immed = *(++istream) & 0xFF; + decoded->src.params.immed = fetch(address, data) & 0xFF; + address += 2; break; case OPSIZE_WORD: - decoded->src.params.immed = *(++istream); + decoded->src.params.immed = fetch(address, data); + address += 2; break; case OPSIZE_LONG: - immed = *(++istream); - decoded->src.params.immed = immed << 16 | *(++istream); + immed = fetch(address, data); + address += 2; + decoded->src.params.immed = immed << 16 | fetch(address, data); + address += 2; break; } - istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); - if (!istream || !m68k_valid_immed_limited_dst(&(decoded->dst))) { + address = m68k_decode_op_ex(opcode, address, fetch, data, opmode, reg, size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_immed_limited_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } @@ -493,24 +533,28 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->op = M68K_SUB; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->extra.size = size = (*istream >> 6) & 3; - reg = *istream & 0x7; - opmode = (*istream >> 3) & 0x7; + decoded->extra.size = size = (opcode >> 6) & 3; + reg = opcode & 0x7; + opmode = (opcode >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: - decoded->src.params.immed = *(++istream) & 0xFF; + decoded->src.params.immed = fetch(address, data) & 0xFF; + address += 2; break; case OPSIZE_WORD: - decoded->src.params.immed = *(++istream); + decoded->src.params.immed = fetch(address, data); + address += 2; break; case OPSIZE_LONG: - immed = *(++istream); - decoded->src.params.immed = immed << 16 | *(++istream); + immed = fetch(address, data); + address += 2; + decoded->src.params.immed = immed << 16 | fetch(address, data); + address += 2; break; } - istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); - if (!istream || !m68k_valid_immed_limited_dst(&(decoded->dst))) { + address = m68k_decode_op_ex(opcode, address, fetch, data, opmode, reg, size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_immed_limited_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } @@ -519,24 +563,28 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->op = M68K_ADD; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->extra.size = size = (*istream >> 6) & 3; - reg = *istream & 0x7; - opmode = (*istream >> 3) & 0x7; + decoded->extra.size = size = (opcode >> 6) & 3; + reg = opcode & 0x7; + opmode = (opcode >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: - decoded->src.params.immed = *(++istream) & 0xFF; + decoded->src.params.immed = fetch(address, data) & 0xFF; + address += 2; break; case OPSIZE_WORD: - decoded->src.params.immed = *(++istream); + decoded->src.params.immed = fetch(address, data); + address += 2; break; case OPSIZE_LONG: - immed = *(++istream); - decoded->src.params.immed = immed << 16 | *(++istream); + immed = fetch(address, data); + address += 2; + decoded->src.params.immed = immed << 16 | fetch(address, data); + address += 2; break; } - istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); - if (!istream || !m68k_valid_immed_limited_dst(&(decoded->dst))) { + address = m68k_decode_op_ex(opcode, address, fetch, data, opmode, reg, size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_immed_limited_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } @@ -546,7 +594,7 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) //Seems like this should be unnecessary since bit instructions are explicitly handled above //Possible this is redundant or the case above is overly restrictive //TODO: Investigate whether this can be removed - switch ((*istream >> 6) & 0x3) + switch ((opcode >> 6) & 0x3) { case 0: decoded->op = M68K_BTST; @@ -562,10 +610,11 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) break; } decoded->src.addr_mode = MODE_IMMEDIATE_WORD; - decoded->src.params.immed = *(++istream) & 0xFF; - istream = m68k_decode_op(istream, OPSIZE_BYTE, &(decoded->dst)); + decoded->src.params.immed = fetch(address, data) & 0xFF; + address += 2; + address = m68k_decode_op(opcode, address, fetch, data, OPSIZE_BYTE, &(decoded->dst)); if ( - !istream || !m68k_valid_immed_dst(&decoded->dst) + address == INVALID_ADDRESS || !m68k_valid_immed_dst(&decoded->dst) || (decoded->op != M68K_BTST && !m68k_valid_immed_limited_dst(&decoded->dst)) ) { decoded->op = M68K_INVALID; @@ -574,38 +623,44 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) break; case 5: //EORI, EORI to CCR, EORI to SR - if ((*istream & 0xFF) == 0x3C) { + if ((opcode & 0xFF) == 0x3C) { decoded->op = M68K_EORI_CCR; decoded->extra.size = OPSIZE_BYTE; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->src.params.immed = *(++istream) & 0xFF; - } else if((*istream & 0xFF) == 0x7C) { + decoded->src.params.immed = fetch(address, data) & 0xFF; + address += 2; + } else if((opcode & 0xFF) == 0x7C) { decoded->op = M68K_EORI_SR; decoded->extra.size = OPSIZE_WORD; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->src.params.immed = *(++istream); + decoded->src.params.immed = fetch(address, data); + address += 2; } else { decoded->op = M68K_EOR; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->extra.size = size = (*istream >> 6) & 3; - reg = *istream & 0x7; - opmode = (*istream >> 3) & 0x7; + decoded->extra.size = size = (opcode >> 6) & 3; + reg = opcode & 0x7; + opmode = (opcode >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: - decoded->src.params.immed = *(++istream) & 0xFF; + decoded->src.params.immed = fetch(address, data) & 0xFF; + address += 2; break; case OPSIZE_WORD: - decoded->src.params.immed = *(++istream); + decoded->src.params.immed = fetch(address, data); + address += 2; break; case OPSIZE_LONG: - immed = *(++istream); - decoded->src.params.immed = immed << 16 | *(++istream); + immed = fetch(address, data); + address += 2; + decoded->src.params.immed = immed << 16 | fetch(address, data); + address += 2; break; } - istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); - if (!istream || !m68k_valid_immed_limited_dst(&(decoded->dst))) { + address = m68k_decode_op_ex(opcode, address, fetch, data, opmode, reg, size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_immed_limited_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } @@ -614,25 +669,29 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) case 6: decoded->op = M68K_CMP; decoded->variant = VAR_IMMEDIATE; - decoded->extra.size = (*istream >> 6) & 0x3; + decoded->extra.size = (opcode >> 6) & 0x3; decoded->src.addr_mode = MODE_IMMEDIATE; - reg = *istream & 0x7; - opmode = (*istream >> 3) & 0x7; + reg = opcode & 0x7; + opmode = (opcode >> 3) & 0x7; switch (decoded->extra.size) { case OPSIZE_BYTE: - decoded->src.params.immed = *(++istream) & 0xFF; + decoded->src.params.immed = fetch(address, data) & 0xFF; + address += 2; break; case OPSIZE_WORD: - decoded->src.params.immed = *(++istream); + decoded->src.params.immed = fetch(address, data); + address += 2; break; case OPSIZE_LONG: - immed = *(++istream); - decoded->src.params.immed = (immed << 16) | *(++istream); + immed = fetch(address, data); + address += 2; + decoded->src.params.immed = (immed << 16) | fetch(address, data); + address += 2; break; } - istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst)); - if (!istream || !m68k_valid_immed_limited_dst(&(decoded->dst))) { + address = m68k_decode_op_ex(opcode, address, fetch, data, opmode, reg, decoded->extra.size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_immed_limited_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } @@ -640,16 +699,17 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) case 7: #ifdef M68010 decoded->op = M68K_MOVES; - decoded->extra.size = *istream >> 6 & 0x3; - immed = *(++istream); + decoded->extra.size = opcode >> 6 & 0x3; + immed = fetch(address, data); + address += 2; reg = immed >> 12 & 0x7; opmode = immed & 0x8000 ? MODE_AREG : MODE_REG; if (immed & 0x800) { decoded->src.addr_mode = opmode; decoded->src.params.regs.pri = reg; - m68k_decode_op_ex(istream, *start >> 3 & 0x7, *start & 0x7, decoded->extra.size, &(decoded->dst)); + address = m68k_decode_op_ex(opcode, address, fetch, data, *start >> 3 & 0x7, *start & 0x7, decoded->extra.size, &(decoded->dst)); } else { - m68k_decode_op_ex(istream, *start >> 3 & 0x7, *start & 0x7, decoded->extra.size, &(decoded->src)); + address = m68k_decode_op_ex(opcode, address, fetch, data, *start >> 3 & 0x7, *start & 0x7, decoded->extra.size, &(decoded->src)); decoded->dst.addr_mode = opmode; decoded->dst.params.regs.pri = reg; } @@ -663,29 +723,29 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) case MOVE_WORD: decoded->op = M68K_MOVE; decoded->extra.size = optype == MOVE_BYTE ? OPSIZE_BYTE : (optype == MOVE_WORD ? OPSIZE_WORD : OPSIZE_LONG); - opmode = (*istream >> 6) & 0x7; - reg = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); - if (!istream || (decoded->src.addr_mode == MODE_AREG && optype == MOVE_BYTE)) { + opmode = (opcode >> 6) & 0x7; + reg = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, decoded->extra.size, &(decoded->src)); + if (address == INVALID_ADDRESS || (decoded->src.addr_mode == MODE_AREG && optype == MOVE_BYTE)) { decoded->op = M68K_INVALID; break; } - istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst)); - if (!istream || decoded->dst.addr_mode > MODE_ABSOLUTE || (decoded->dst.addr_mode == MODE_AREG && optype == MOVE_BYTE)) { + address = m68k_decode_op_ex(opcode, address, fetch, data, opmode, reg, decoded->extra.size, &(decoded->dst)); + if (address == INVALID_ADDRESS || decoded->dst.addr_mode > MODE_ABSOLUTE || (decoded->dst.addr_mode == MODE_AREG && optype == MOVE_BYTE)) { decoded->op = M68K_INVALID; break; } break; case MISC: - if ((*istream & 0x1C0) == 0x1C0) { + if ((opcode & 0x1C0) == 0x1C0) { decoded->op = M68K_LEA; decoded->extra.size = OPSIZE_LONG; decoded->dst.addr_mode = MODE_AREG; - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, decoded->extra.size, &(decoded->src)); if ( - !istream || decoded->src.addr_mode == MODE_REG || decoded->src.addr_mode == MODE_AREG + address == INVALID_ADDRESS || decoded->src.addr_mode == MODE_REG || decoded->src.addr_mode == MODE_AREG || decoded->src.addr_mode == MODE_AREG_POSTINC || decoded->src.addr_mode == MODE_AREG_PREDEC || decoded->src.addr_mode == MODE_IMMEDIATE ) { @@ -693,9 +753,9 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) break; } } else { - if (*istream & 0x100) { + if (opcode & 0x100) { decoded->op = M68K_CHK; - if ((*istream & 0x180) == 0x180) { + if ((opcode & 0x180) == 0x180) { decoded->extra.size = OPSIZE_WORD; } else { //only on M68020+ @@ -707,24 +767,25 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) #endif } decoded->dst.addr_mode = MODE_REG; - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); - if (!istream || decoded->src.addr_mode == MODE_AREG) { + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, decoded->extra.size, &(decoded->src)); + if (address == INVALID_ADDRESS || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } } else { - opmode = (*istream >> 3) & 0x7; - if ((*istream & 0xB80) == 0x880 && opmode != MODE_REG && opmode != MODE_AREG) { + opmode = (opcode >> 3) & 0x7; + if ((opcode & 0xB80) == 0x880 && opmode != MODE_REG && opmode != MODE_AREG) { //TODO: Check for invalid modes that are dependent on direction decoded->op = M68K_MOVEM; - decoded->extra.size = *istream & 0x40 ? OPSIZE_LONG : OPSIZE_WORD; - reg = *istream & 0x7; - if(*istream & 0x400) { + decoded->extra.size = opcode & 0x40 ? OPSIZE_LONG : OPSIZE_WORD; + reg = opcode & 0x7; + if(opcode & 0x400) { decoded->dst.addr_mode = MODE_REG; - decoded->dst.params.immed = *(++istream); - istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->src)); - if (!istream || decoded->src.addr_mode == MODE_AREG_PREDEC || decoded->src.addr_mode == MODE_IMMEDIATE) { + decoded->dst.params.immed = fetch(address, data); + address += 2; + address = m68k_decode_op_ex(opcode, address, fetch, data, opmode, reg, decoded->extra.size, &(decoded->src)); + if (address == INVALID_ADDRESS || decoded->src.addr_mode == MODE_AREG_PREDEC || decoded->src.addr_mode == MODE_IMMEDIATE) { decoded->op = M68K_INVALID; break; } @@ -734,16 +795,17 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) } } else { decoded->src.addr_mode = MODE_REG; - decoded->src.params.immed = *(++istream); - istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst)); - if (!istream || !m68k_valid_movem_dst(&decoded->dst)) { + decoded->src.params.immed = fetch(address, data); + address += 2; + address = m68k_decode_op_ex(opcode, address, fetch, data, opmode, reg, decoded->extra.size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_movem_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } } } else { - optype = (*istream >> 9) & 0x7; - size = (*istream >> 6) & 0x3; + optype = (opcode >> 9) & 0x7; + size = (opcode >> 6) & 0x3; switch(optype) { case 0: @@ -755,8 +817,8 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->op = M68K_NEGX; } decoded->extra.size = size; - istream= m68k_decode_op(istream, size, &(decoded->dst)); - if (!istream || !m68k_valid_immed_limited_dst(&decoded->dst)) { + address = m68k_decode_op(opcode, address, fetch, data, size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_immed_limited_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } @@ -774,8 +836,8 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->op = M68K_CLR; } decoded->extra.size = size; - istream= m68k_decode_op(istream, size, &(decoded->dst)); - if (!istream || !m68k_valid_immed_limited_dst(&decoded->dst)) { + address = m68k_decode_op(opcode, address, fetch, data, size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_immed_limited_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } @@ -785,15 +847,15 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) if (size == OPSIZE_INVALID) { decoded->op = M68K_MOVE_CCR; size = OPSIZE_WORD; - istream= m68k_decode_op(istream, size, &(decoded->src)); - if (!istream || decoded->src.addr_mode == MODE_AREG) { + address = m68k_decode_op(opcode, address, fetch, data, size, &(decoded->src)); + if (address == INVALID_ADDRESS || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } } else { decoded->op = M68K_NEG; - istream= m68k_decode_op(istream, size, &(decoded->dst)); - if (!istream || !m68k_valid_immed_limited_dst(&decoded->dst)) { + address = m68k_decode_op(opcode, address, fetch, data, size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_immed_limited_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } @@ -805,15 +867,15 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) if (size == OPSIZE_INVALID) { decoded->op = M68K_MOVE_SR; size = OPSIZE_WORD; - istream= m68k_decode_op(istream, size, &(decoded->src)); - if (!istream || decoded->src.addr_mode == MODE_AREG) { + address = m68k_decode_op(opcode, address, fetch, data, size, &(decoded->src)); + if (address == INVALID_ADDRESS || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } } else { decoded->op = M68K_NOT; - istream= m68k_decode_op(istream, size, &(decoded->dst)); - if (!istream || !m68k_valid_immed_limited_dst(&decoded->dst)) { + address = m68k_decode_op(opcode, address, fetch, data, size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_immed_limited_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } @@ -822,21 +884,23 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) break; case 4: //EXT, EXTB, LINK.l, NBCD, SWAP, BKPT, PEA - switch((*istream >> 3) & 0x3F) + switch((opcode >> 3) & 0x3F) { case 1: #ifdef M68020 decoded->op = M68K_LINK; decoded->extra.size = OPSIZE_LONG; - reg = *istream & 0x7; - immed = *(++istream) << 16; - immed |= *(++istream); + reg = opcode & 0x7; + immed = fetch(address, data) << 16; + address += 2; + immed |= fetch(address, data); + address += 2; #endif break; case 8: decoded->op = M68K_SWAP; decoded->src.addr_mode = MODE_REG; - decoded->src.params.regs.pri = *istream & 0x7; + decoded->src.params.regs.pri = opcode & 0x7; decoded->extra.size = OPSIZE_WORD; break; case 9: @@ -844,19 +908,19 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->op = M68K_BKPT; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->extra.size = OPSIZE_UNSIZED; - decoded->src.params.immed = *istream & 0x7; + decoded->src.params.immed = opcode & 0x7; #endif break; case 0x10: decoded->op = M68K_EXT; decoded->dst.addr_mode = MODE_REG; - decoded->dst.params.regs.pri = *istream & 0x7; + decoded->dst.params.regs.pri = opcode & 0x7; decoded->extra.size = OPSIZE_WORD; break; case 0x18: decoded->op = M68K_EXT; decoded->dst.addr_mode = MODE_REG; - decoded->dst.params.regs.pri = *istream & 0x7; + decoded->dst.params.regs.pri = opcode & 0x7; decoded->extra.size = OPSIZE_LONG; break; case 0x38: @@ -864,20 +928,20 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) #endif break; default: - if (!(*istream & 0x1C0)) { + if (!(opcode & 0x1C0)) { decoded->op = M68K_NBCD; decoded->extra.size = OPSIZE_BYTE; - istream = m68k_decode_op(istream, OPSIZE_BYTE, &(decoded->dst)); - if (!istream || !m68k_valid_immed_limited_dst(&decoded->dst)) { + address = m68k_decode_op(opcode, address, fetch, data, OPSIZE_BYTE, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_immed_limited_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } - } else if((*istream & 0x1C0) == 0x40) { + } else if((opcode & 0x1C0) == 0x40) { decoded->op = M68K_PEA; decoded->extra.size = OPSIZE_LONG; - istream = m68k_decode_op(istream, OPSIZE_LONG, &(decoded->src)); + address = m68k_decode_op(opcode, address, fetch, data, OPSIZE_LONG, &(decoded->src)); if ( - !istream || decoded->src.addr_mode == MODE_REG || decoded->src.addr_mode == MODE_AREG + address == INVALID_ADDRESS || decoded->src.addr_mode == MODE_REG || decoded->src.addr_mode == MODE_AREG || decoded->src.addr_mode == MODE_AREG_POSTINC || decoded->src.addr_mode == MODE_AREG_PREDEC || decoded->src.addr_mode == MODE_IMMEDIATE ) { @@ -889,7 +953,7 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) break; case 5: //BGND, ILLEGAL, TAS, TST - optype = *istream & 0xFF; + optype = opcode & 0xFF; if (optype == 0xFA) { //BGND - CPU32 only } else if (optype == 0xFC) { @@ -899,16 +963,16 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) if (size == OPSIZE_INVALID) { decoded->op = M68K_TAS; decoded->extra.size = OPSIZE_BYTE; - istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->dst)); - if (!istream || !m68k_valid_immed_limited_dst(&decoded->dst)) { + address = m68k_decode_op(opcode, address, fetch, data, decoded->extra.size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_immed_limited_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } } else { decoded->op = M68K_TST; decoded->extra.size = size; - istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); - if (!istream) { + address = m68k_decode_op(opcode, address, fetch, data, decoded->extra.size, &(decoded->src)); + if (address == INVALID_ADDRESS) { decoded->op = M68K_INVALID; break; } @@ -929,17 +993,17 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) break; case 7: //TRAP, LINK.w, UNLNK, MOVE USP, RESET, NOP, STOP, RTE, RTD, RTS, TRAPV, RTR, MOVEC, JSR, JMP - if (*istream & 0x80) { + if (opcode & 0x80) { //JSR, JMP - if (*istream & 0x40) { + if (opcode & 0x40) { decoded->op = M68K_JMP; } else { decoded->op = M68K_JSR; } decoded->extra.size = OPSIZE_UNSIZED; - istream = m68k_decode_op(istream, OPSIZE_UNSIZED, &(decoded->src)); + address = m68k_decode_op(opcode, address, fetch, data, OPSIZE_UNSIZED, &(decoded->src)); if ( - !istream + address == INVALID_ADDRESS || (decoded->src.addr_mode < MODE_AREG_DISPLACE && decoded->src.addr_mode != MODE_AREG_INDIRECT) || decoded->src.addr_mode == MODE_IMMEDIATE ) { @@ -948,11 +1012,11 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) } } else { //it would appear bit 6 needs to be set for it to be a valid instruction here - if (!(*istream & 0x40)) { + if (!(opcode & 0x40)) { decoded->op = M68K_INVALID; break; } - switch((*istream >> 3) & 0x7) + switch((opcode >> 3) & 0x7) { case 0: case 1: @@ -960,39 +1024,40 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->op = M68K_TRAP; decoded->extra.size = OPSIZE_UNSIZED; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->src.params.immed = *istream & 0xF; + decoded->src.params.immed = opcode & 0xF; break; case 2: //LINK.w decoded->op = M68K_LINK; decoded->extra.size = OPSIZE_WORD; decoded->src.addr_mode = MODE_AREG; - decoded->src.params.regs.pri = *istream & 0x7; + decoded->src.params.regs.pri = opcode & 0x7; decoded->dst.addr_mode = MODE_IMMEDIATE; - decoded->dst.params.immed = sign_extend16(*(++istream)); + decoded->dst.params.immed = sign_extend16(fetch(address, data)); + address += 2; break; case 3: //UNLK decoded->op = M68K_UNLK; decoded->extra.size = OPSIZE_UNSIZED; decoded->dst.addr_mode = MODE_AREG; - decoded->dst.params.regs.pri = *istream & 0x7; + decoded->dst.params.regs.pri = opcode & 0x7; break; case 4: case 5: //MOVE USP decoded->op = M68K_MOVE_USP; - if (*istream & 0x8) { + if (opcode & 0x8) { decoded->dst.addr_mode = MODE_AREG; - decoded->dst.params.regs.pri = *istream & 0x7; + decoded->dst.params.regs.pri = opcode & 0x7; } else { decoded->src.addr_mode = MODE_AREG; - decoded->src.params.regs.pri = *istream & 0x7; + decoded->src.params.regs.pri = opcode & 0x7; } break; case 6: decoded->extra.size = OPSIZE_UNSIZED; - switch(*istream & 0x7) + switch(opcode & 0x7) { case 0: decoded->op = M68K_RESET; @@ -1003,7 +1068,8 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) case 2: decoded->op = M68K_STOP; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->src.params.immed =*(++istream); + decoded->src.params.immed =fetch(address, data); + address += 2; break; case 3: decoded->op = M68K_RTE; @@ -1012,7 +1078,8 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) #ifdef M68010 decoded->op = M68K_RTD; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->src.params.immed =*(++istream); + decoded->src.params.immed =fetch(address, data); + address += 2; #endif break; case 5: @@ -1030,7 +1097,8 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) //MOVEC #ifdef M68010 decoded->op = M68K_MOVEC; - immed = *(++istream); + immed = fetch(address, data); + address += 2; reg = immed >> 12 & 0x7; opmode = immed & 0x8000 ? MODE_AREG : MODE_REG; immed &= 0xFFF; @@ -1067,17 +1135,18 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) } break; case QUICK_ARITH_LOOP: - size = (*istream >> 6) & 3; + size = (opcode >> 6) & 3; if (size == 0x3) { //DBcc, TRAPcc or Scc - m68k_decode_cond(*istream, decoded); - if (((*istream >> 3) & 0x7) == 1) { + m68k_decode_cond(opcode, decoded); + if (((opcode >> 3) & 0x7) == 1) { decoded->op = M68K_DBCC; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->dst.addr_mode = MODE_REG; - decoded->dst.params.regs.pri = *istream & 0x7; - decoded->src.params.immed = sign_extend16(*(++istream)); - } else if(((*istream >> 3) & 0x7) == 1 && (*istream & 0x7) > 1 && (*istream & 0x7) < 5) { + decoded->dst.params.regs.pri = opcode & 0x7; + decoded->src.params.immed = sign_extend16(fetch(address, data)); + address += 2; + } else if(((opcode >> 3) & 0x7) == 1 && (opcode & 0x7) > 1 && (opcode & 0x7) < 5) { #ifdef M68020 decoded->op = M68K_TRAPCC; decoded->src.addr_mode = MODE_IMMEDIATE; @@ -1085,9 +1154,8 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) #endif } else { decoded->op = M68K_SCC; - decoded->extra.cond = (*istream >> 8) & 0xF; - istream = m68k_decode_op(istream, OPSIZE_BYTE, &(decoded->dst)); - if (!istream || !m68k_valid_immed_limited_dst(&decoded->dst)) { + address = m68k_decode_op(opcode, address, fetch, data, OPSIZE_BYTE, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_immed_limited_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } @@ -1097,37 +1165,40 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->variant = VAR_QUICK; decoded->extra.size = size; decoded->src.addr_mode = MODE_IMMEDIATE; - immed = m68k_reg_quick_field(*istream); + immed = m68k_reg_quick_field(opcode); if (!immed) { immed = 8; } decoded->src.params.immed = immed; - if (*istream & 0x100) { + if (opcode & 0x100) { decoded->op = M68K_SUB; } else { decoded->op = M68K_ADD; } - istream = m68k_decode_op(istream, size, &(decoded->dst)); - if (!istream || decoded->dst.addr_mode > MODE_ABSOLUTE || (size == OPSIZE_BYTE && decoded->dst.addr_mode == MODE_AREG)) { + address = m68k_decode_op(opcode, address, fetch, data, size, &(decoded->dst)); + if (address == INVALID_ADDRESS || decoded->dst.addr_mode > MODE_ABSOLUTE || (size == OPSIZE_BYTE && decoded->dst.addr_mode == MODE_AREG)) { decoded->op = M68K_INVALID; break; } } break; case BRANCH: - m68k_decode_cond(*istream, decoded); + m68k_decode_cond(opcode, decoded); decoded->op = decoded->extra.cond == COND_FALSE ? M68K_BSR : M68K_BCC; decoded->src.addr_mode = MODE_IMMEDIATE; - immed = *istream & 0xFF; + immed = opcode & 0xFF; if (immed == 0) { decoded->variant = VAR_WORD; - immed = *(++istream); + immed = fetch(address, data); + address += 2; immed = sign_extend16(immed); #ifdef M68020 } else if (immed == 0xFF) { decoded->variant = VAR_LONG; - immed = *(++istream) << 16; - immed |= *(++istream); + immed = fetch(address, data) << 16; + address += 2; + immed |= fetch(address, data); + address += 2; #endif } else { decoded->variant = VAR_BYTE; @@ -1136,7 +1207,7 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->src.params.immed = immed; break; case MOVEQ: - if (*istream & 0x100) { + if (opcode & 0x100) { decoded->op = M68K_INVALID; break; } @@ -1144,25 +1215,25 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->variant = VAR_QUICK; decoded->extra.size = OPSIZE_LONG; decoded->src.addr_mode = MODE_IMMEDIATE; - decoded->src.params.immed = sign_extend8(*istream & 0xFF); + decoded->src.params.immed = sign_extend8(opcode & 0xFF); decoded->dst.addr_mode = MODE_REG; - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - immed = *istream & 0xFF; + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + immed = opcode & 0xFF; break; case OR_DIV_SBCD: //for OR, if opmode bit 2 is 1, then src = Dn, dst = - opmode = (*istream >> 6) & 0x7; + opmode = (opcode >> 6) & 0x7; size = opmode & 0x3; - if (size == OPSIZE_INVALID || (opmode & 0x4 && !(*istream & 0x30))) { + if (size == OPSIZE_INVALID || (opmode & 0x4 && !(opcode & 0x30))) { switch(opmode) { case 3: decoded->op = M68K_DIVU; decoded->extra.size = OPSIZE_WORD; decoded->dst.addr_mode = MODE_REG; - decoded->dst.params.regs.pri = (*istream >> 9) & 0x7; - istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src)); - if (!istream || decoded->src.addr_mode == MODE_AREG) { + decoded->dst.params.regs.pri = (opcode >> 9) & 0x7; + address = m68k_decode_op(opcode, address, fetch, data, OPSIZE_WORD, &(decoded->src)); + if (address == INVALID_ADDRESS || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } @@ -1170,9 +1241,9 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) case 4: decoded->op = M68K_SBCD; decoded->extra.size = OPSIZE_BYTE; - decoded->dst.addr_mode = decoded->src.addr_mode = *istream & 0x8 ? MODE_AREG_PREDEC : MODE_REG; - decoded->src.params.regs.pri = *istream & 0x7; - decoded->dst.params.regs.pri = (*istream >> 9) & 0x7; + decoded->dst.addr_mode = decoded->src.addr_mode = opcode & 0x8 ? MODE_AREG_PREDEC : MODE_REG; + decoded->src.params.regs.pri = opcode & 0x7; + decoded->dst.params.regs.pri = (opcode >> 9) & 0x7; break; case 5: #ifdef M68020 @@ -1186,9 +1257,9 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->op = M68K_DIVS; decoded->extra.size = OPSIZE_WORD; decoded->dst.addr_mode = MODE_REG; - decoded->dst.params.regs.pri = (*istream >> 9) & 0x7; - istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src)); - if (!istream || decoded->src.addr_mode == MODE_AREG) { + decoded->dst.params.regs.pri = (opcode >> 9) & 0x7; + address = m68k_decode_op(opcode, address, fetch, data, OPSIZE_WORD, &(decoded->src)); + if (address == INVALID_ADDRESS || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } @@ -1199,17 +1270,17 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->extra.size = size; if (opmode & 0x4) { decoded->src.addr_mode = MODE_REG; - decoded->src.params.regs.pri = (*istream >> 9) & 0x7; - istream = m68k_decode_op(istream, size, &(decoded->dst)); - if (!istream || !m68k_valid_full_arith_dst(&(decoded->dst))) { + decoded->src.params.regs.pri = (opcode >> 9) & 0x7; + address = m68k_decode_op(opcode, address, fetch, data, size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_full_arith_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } } else { decoded->dst.addr_mode = MODE_REG; - decoded->dst.params.regs.pri = (*istream >> 9) & 0x7; - istream = m68k_decode_op(istream, size, &(decoded->src)); - if (!istream || decoded->src.addr_mode == MODE_AREG) { + decoded->dst.params.regs.pri = (opcode >> 9) & 0x7; + address = m68k_decode_op(opcode, address, fetch, data, size, &(decoded->src)); + if (address == INVALID_ADDRESS || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } @@ -1217,27 +1288,27 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) } break; case SUB_SUBX: - size = (*istream >> 6) & 0x3; + size = (opcode >> 6) & 0x3; decoded->op = M68K_SUB; - if (*istream & 0x100) { + if (opcode & 0x100) { // destination, SUBA.l or SUBX - if (*istream & 0x30 || size == OPSIZE_INVALID) { + if (opcode & 0x30 || size == OPSIZE_INVALID) { if (size == OPSIZE_INVALID) { //SUBA.l decoded->extra.size = OPSIZE_LONG; decoded->dst.addr_mode = MODE_AREG; - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, OPSIZE_LONG, &(decoded->src)); - if (!istream) { + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, OPSIZE_LONG, &(decoded->src)); + if (address == INVALID_ADDRESS) { decoded->op = M68K_INVALID; break; } } else { decoded->extra.size = size; decoded->src.addr_mode = MODE_REG; - decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, size, &(decoded->dst)); - if (!istream || !m68k_valid_full_arith_dst(&decoded->dst)) { + decoded->src.params.regs.pri = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_full_arith_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } @@ -1246,9 +1317,9 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) //SUBX decoded->op = M68K_SUBX; decoded->extra.size = size; - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - decoded->src.params.regs.pri = *istream & 0x7; - if (*istream & 0x8) { + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + decoded->src.params.regs.pri = opcode & 0x7; + if (opcode & 0x8) { decoded->dst.addr_mode = decoded->src.addr_mode = MODE_AREG_PREDEC; } else { decoded->dst.addr_mode = decoded->src.addr_mode = MODE_REG; @@ -1263,9 +1334,9 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->extra.size = size; decoded->dst.addr_mode = MODE_REG; } - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); - if (!istream || (decoded->src.addr_mode == MODE_AREG && decoded->extra.size == OPSIZE_BYTE)) { + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, decoded->extra.size, &(decoded->src)); + if (address == INVALID_ADDRESS || (decoded->src.addr_mode == MODE_AREG && decoded->extra.size == OPSIZE_BYTE)) { decoded->op = M68K_INVALID; break; } @@ -1275,23 +1346,23 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->op = M68K_A_LINE_TRAP; break; case CMP_XOR: - size = (*istream >> 6) & 0x3; + size = (opcode >> 6) & 0x3; decoded->op = M68K_CMP; - if (*istream & 0x100) { + if (opcode & 0x100) { //CMPM or CMPA.l or EOR if (size == OPSIZE_INVALID) { decoded->extra.size = OPSIZE_LONG; decoded->dst.addr_mode = MODE_AREG; - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); - if (!istream) { + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, decoded->extra.size, &(decoded->src)); + if (address == INVALID_ADDRESS) { decoded->op = M68K_INVALID; break; } } else { - reg = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, size, &(decoded->dst)); - if (!istream) { + reg = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, size, &(decoded->dst)); + if (address == INVALID_ADDRESS) { decoded->op = M68K_INVALID; break; } @@ -1320,9 +1391,9 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->extra.size = size; decoded->dst.addr_mode = MODE_REG; } - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); - if (!istream || (decoded->src.addr_mode == MODE_AREG && decoded->extra.size == OPSIZE_BYTE)) { + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, decoded->extra.size, &(decoded->src)); + if (address == INVALID_ADDRESS || (decoded->src.addr_mode == MODE_AREG && decoded->extra.size == OPSIZE_BYTE)) { decoded->op = M68K_INVALID; break; } @@ -1338,36 +1409,36 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) //operand order bit + 2 size bits (00 - 10) //no address register direct addressing //data register direct not allowed when is the source (operand order bit of 1) - if (*istream & 0x100) { - if ((*istream & 0xC0) == 0xC0) { + if (opcode & 0x100) { + if ((opcode & 0xC0) == 0xC0) { decoded->op = M68K_MULS; decoded->extra.size = OPSIZE_WORD; decoded->dst.addr_mode = MODE_REG; - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src)); - if (!istream || decoded->src.addr_mode == MODE_AREG) { + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, OPSIZE_WORD, &(decoded->src)); + if (address == INVALID_ADDRESS || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } - } else if(!(*istream & 0xF0)) { + } else if(!(opcode & 0xF0)) { decoded->op = M68K_ABCD; decoded->extra.size = OPSIZE_BYTE; - decoded->src.params.regs.pri = *istream & 0x7; - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - decoded->dst.addr_mode = decoded->src.addr_mode = (*istream & 8) ? MODE_AREG_PREDEC : MODE_REG; - } else if(!(*istream & 0x30)) { + decoded->src.params.regs.pri = opcode & 0x7; + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + decoded->dst.addr_mode = decoded->src.addr_mode = (opcode & 8) ? MODE_AREG_PREDEC : MODE_REG; + } else if(!(opcode & 0x30)) { decoded->op = M68K_EXG; decoded->extra.size = OPSIZE_LONG; - decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); - decoded->dst.params.regs.pri = *istream & 0x7; - if (*istream & 0x8) { - if (*istream & 0x80) { + decoded->src.params.regs.pri = m68k_reg_quick_field(opcode); + decoded->dst.params.regs.pri = opcode & 0x7; + if (opcode & 0x8) { + if (opcode & 0x80) { decoded->src.addr_mode = MODE_REG; decoded->dst.addr_mode = MODE_AREG; } else { decoded->src.addr_mode = decoded->dst.addr_mode = MODE_AREG; } - } else if (*istream & 0x40) { + } else if (opcode & 0x40) { decoded->src.addr_mode = decoded->dst.addr_mode = MODE_REG; } else { decoded->op = M68K_INVALID; @@ -1375,33 +1446,33 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) } } else { decoded->op = M68K_AND; - decoded->extra.size = (*istream >> 6) & 0x3; + decoded->extra.size = (opcode >> 6) & 0x3; decoded->src.addr_mode = MODE_REG; - decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->dst)); - if (!istream || !m68k_valid_full_arith_dst(&(decoded->dst))) { + decoded->src.params.regs.pri = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, decoded->extra.size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_full_arith_dst(&(decoded->dst))) { decoded->op = M68K_INVALID; break; } } } else { - if ((*istream & 0xC0) == 0xC0) { + if ((opcode & 0xC0) == 0xC0) { decoded->op = M68K_MULU; decoded->extra.size = OPSIZE_WORD; decoded->dst.addr_mode = MODE_REG; - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src)); - if (!istream || decoded->src.addr_mode == MODE_AREG) { + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, OPSIZE_WORD, &(decoded->src)); + if (address == INVALID_ADDRESS || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } } else { decoded->op = M68K_AND; - decoded->extra.size = (*istream >> 6) & 0x3; + decoded->extra.size = (opcode >> 6) & 0x3; decoded->dst.addr_mode = MODE_REG; - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); - if (!istream || decoded->src.addr_mode == MODE_AREG) { + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, decoded->extra.size, &(decoded->src)); + if (address == INVALID_ADDRESS || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; break; } @@ -1409,27 +1480,27 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) } break; case ADD_ADDX: - size = (*istream >> 6) & 0x3; + size = (opcode >> 6) & 0x3; decoded->op = M68K_ADD; - if (*istream & 0x100) { + if (opcode & 0x100) { // destination, ADDA.l or ADDX - if (*istream & 0x30 || size == OPSIZE_INVALID) { + if (opcode & 0x30 || size == OPSIZE_INVALID) { if (size == OPSIZE_INVALID) { //ADDA.l decoded->extra.size = OPSIZE_LONG; decoded->dst.addr_mode = MODE_AREG; - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, OPSIZE_LONG, &(decoded->src)); - if (!istream) { + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, OPSIZE_LONG, &(decoded->src)); + if (address == INVALID_ADDRESS) { decoded->op = M68K_INVALID; break; } } else { decoded->extra.size = size; decoded->src.addr_mode = MODE_REG; - decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, size, &(decoded->dst)); - if (!istream || !m68k_valid_full_arith_dst(&decoded->dst)) { + decoded->src.params.regs.pri = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, size, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_full_arith_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } @@ -1438,9 +1509,9 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) //ADDX decoded->op = M68K_ADDX; decoded->extra.size = size; - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - decoded->src.params.regs.pri = *istream & 0x7; - if (*istream & 0x8) { + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + decoded->src.params.regs.pri = opcode & 0x7; + if (opcode & 0x8) { decoded->dst.addr_mode = decoded->src.addr_mode = MODE_AREG_PREDEC; } else { decoded->dst.addr_mode = decoded->src.addr_mode = MODE_REG; @@ -1455,17 +1526,17 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->extra.size = size; decoded->dst.addr_mode = MODE_REG; } - decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); - istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); - if (!istream || (decoded->src.addr_mode == MODE_AREG && decoded->extra.size == OPSIZE_BYTE)) { + decoded->dst.params.regs.pri = m68k_reg_quick_field(opcode); + address = m68k_decode_op(opcode, address, fetch, data, decoded->extra.size, &(decoded->src)); + if (address == INVALID_ADDRESS || (decoded->src.addr_mode == MODE_AREG && decoded->extra.size == OPSIZE_BYTE)) { decoded->op = M68K_INVALID; break; } } break; case SHIFT_ROTATE: - if ((*istream & 0x8C0) == 0xC0) { - switch((*istream >> 8) & 0x7) + if ((opcode & 0x8C0) == 0xC0) { + switch((opcode >> 8) & 0x7) { case 0: decoded->op = M68K_ASR; @@ -1493,13 +1564,13 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) break; } decoded->extra.size = OPSIZE_WORD; - istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->dst)); - if (!istream || !m68k_valid_full_arith_dst(&decoded->dst)) { + address = m68k_decode_op(opcode, address, fetch, data, OPSIZE_WORD, &(decoded->dst)); + if (address == INVALID_ADDRESS || !m68k_valid_full_arith_dst(&decoded->dst)) { decoded->op = M68K_INVALID; break; } - } else if((*istream & 0xC0) != 0xC0) { - switch(((*istream >> 2) & 0x6) | ((*istream >> 8) & 1)) + } else if((opcode & 0xC0) != 0xC0) { + switch(((opcode >> 2) & 0x6) | ((opcode >> 8) & 1)) { case 0: decoded->op = M68K_ASR; @@ -1526,9 +1597,9 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->op = M68K_ROL; break; } - decoded->extra.size = (*istream >> 6) & 0x3; - immed = (*istream >> 9) & 0x7; - if (*istream & 0x20) { + decoded->extra.size = (opcode >> 6) & 0x3; + immed = (opcode >> 9) & 0x7; + if (opcode & 0x20) { decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = immed; } else { @@ -1540,12 +1611,12 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->variant = VAR_QUICK; } decoded->dst.addr_mode = MODE_REG; - decoded->dst.params.regs.pri = *istream & 0x7; + decoded->dst.params.regs.pri = opcode & 0x7; } else { #ifdef M68020 //TODO: Implement bitfield instructions for M68020+ support - switch (*istream >> 8 & 7) + switch (opcode >> 8 & 7) { case 0: decoded->op = M68K_BFTST; // @@ -1572,8 +1643,8 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) decoded->op = M68K_BFINS; //Dn, break; } - opmode = *istream >> 3 & 0x7; - reg = *istream & 0x7; + opmode = opcode >> 3 & 0x7; + reg = opcode & 0x7; m68k_op_info *ea, *other; if (decoded->op == M68K_BFEXTU || decoded->op == M68K_BFEXTS || decoded->op == M68K_BFFFO) { @@ -1583,16 +1654,18 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) ea = &(decoded->dst); other = &(decoded->dst); } - if (*istream & 0x100) + if (opcode & 0x100) { - immed = *(istream++); + immed = fetch(address, data); + address += 2; other->addr_mode = MODE_REG; other->params.regs.pri = immed >> 12 & 0x7; } else { - immed = *(istream++); + immed = fetch(address, data); + address += 2; } decoded->extra.size = OPSIZE_UNSIZED; - istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, ea); + address = m68k_decode_op_ex(opcode, address, fetch, data, opmode, reg, decoded->extra.size, ea); ea->addr_mode |= M68K_FLAG_BITFIELD; ea->bitfield = immed & 0xFFF; #endif @@ -1604,12 +1677,12 @@ uint16_t * m68k_decode(uint16_t * istream, m68kinst * decoded, uint32_t address) break; } if (decoded->op == M68K_INVALID) { - decoded->src.params.immed = *start; + decoded->src.params.immed = opcode; decoded->bytes = 2; - return start + 1; + return start_address + 2; } - decoded->bytes = 2 * (istream + 1 - start); - return istream+1; + decoded->bytes = address - start_address; + return address; } uint32_t m68k_branch_target(m68kinst * inst, uint32_t *dregs, uint32_t *aregs) @@ -2517,12 +2590,7 @@ int m68k_disasm_movem_op(m68k_op_info *decoded, m68k_op_info *other, char *dst, } } -int m68k_default_label_fun(char * dst, uint32_t address, void * data) -{ - return sprintf(dst, "ADR_%X", address); -} - -int m68k_disasm_ex(m68kinst * decoded, char * dst, uint8_t labels, format_label_fun label_fun, void * data) +int m68k_disasm_ex(m68kinst * decoded, char * dst, uint8_t labels, format_label_fun label_fun, disasm_context *data) { int ret,op1len; uint8_t size; @@ -2609,7 +2677,7 @@ int m68k_disasm_ex(m68kinst * decoded, char * dst, uint8_t labels, format_label_ #endif default: size = decoded->extra.size; - uint8_t is_quick = decoded->variant == VAR_QUICK && decoded->op != M68K_ASL && decoded->op != M68K_ASR + uint8_t is_quick = decoded->variant == VAR_QUICK && decoded->op != M68K_ASL && decoded->op != M68K_ASR && decoded->op != M68K_LSL && decoded->op != M68K_LSR && decoded->op != M68K_ROXR && decoded->op != M68K_ROXL && decoded->op != M68K_ROR && decoded->op != M68K_ROL; ret = sprintf(dst, "%s%s%s", @@ -2634,11 +2702,7 @@ int m68k_disasm(m68kinst * decoded, char * dst) return m68k_disasm_ex(decoded, dst, 0, NULL, NULL); } -int m68k_disasm_labels(m68kinst * decoded, char * dst, format_label_fun label_fun, void * data) +int m68k_disasm_labels(m68kinst * decoded, char * dst, disasm_context *disasm) { - if (!label_fun) - { - label_fun = m68k_default_label_fun; - } - return m68k_disasm_ex(decoded, dst, 1, label_fun, data); + return m68k_disasm_ex(decoded, dst, 1, format_label, disasm); } diff --git a/68kinst.h b/68kinst.h index 3d3ecef..dc81c3b 100644 --- a/68kinst.h +++ b/68kinst.h @@ -7,6 +7,7 @@ #define M68KINST_H_ #include +#include "disasm.h" #ifdef M68030 #define M68020 @@ -333,15 +334,14 @@ typedef enum { VECTOR_USER0 = 64 } m68k_vector; -typedef int (*format_label_fun)(char * dst, uint32_t address, void * data); +typedef uint16_t (*m68k_fetch_fun)(uint32_t address, void *data); -uint16_t * m68k_decode(uint16_t * istream, m68kinst * dst, uint32_t address); +uint32_t m68k_decode(m68k_fetch_fun fetch, void *data, m68kinst * dst, uint32_t address); uint32_t m68k_branch_target(m68kinst * inst, uint32_t *dregs, uint32_t *aregs); uint8_t m68k_is_branch(m68kinst * inst); uint8_t m68k_is_noncall_branch(m68kinst * inst); int m68k_disasm(m68kinst * decoded, char * dst); -int m68k_disasm_labels(m68kinst * decoded, char * dst, format_label_fun label_fun, void * data); -int m68k_default_label_fun(char * dst, uint32_t address, void * data); +int m68k_disasm_labels(m68kinst * decoded, char * dst, disasm_context *disasm); #endif diff --git a/Android.mk b/Android.mk index c84f7f2..3e63603 100644 --- a/Android.mk +++ b/Android.mk @@ -4,25 +4,28 @@ include $(CLEAR_VARS) LOCAL_MODULE := main -SDL_PATH := android/jni/SDL +SDL_PATH := android/app/jni/SDL LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include -LOCAL_CFLAGS += -std=gnu99 -DX86_32 -DUSE_GLES +LOCAL_CFLAGS += -std=gnu99 -DNEW_CORE -DUSE_GLES # Add your application source files here... -LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \ - 68kinst.c debug.c gst.c psg.c z80_to_x86.c backend.c io.c render_sdl.c \ - tern.c backend_x86.c gdb_remote.c m68k_core.c romdb.c m68k_core_x86.c \ - util.c wave.c blastem.c gen.c mem.c vdp.c ym2612.c config.c gen_x86.c \ - terminal.c z80inst.c menu.c arena.c zlib/adler32.c zlib/compress.c \ - zlib/crc32.c zlib/deflate.c zlib/gzclose.c zlib/gzlib.c zlib/gzread.c \ - zlib/gzwrite.c zlib/infback.c zlib/inffast.c zlib/inflate.c \ - zlib/inftrees.c zlib/trees.c zlib/uncompr.c zlib/zutil.c \ - nuklear_ui/font_android.c nuklear_ui/blastem_nuklear.c nuklear_ui/sfnt.c \ - ppm.c controller_info.c png.c system.c genesis.c sms.c serialize.c \ - saves.c hash.c xband.c zip.c bindings.c jcart.c paths.c megawifi.c \ - nor.c i2c.c sega_mapper.c realtec.c multi_game.c net.c +LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c 68kinst.c \ + debug.c gst.c psg.c z80.c backend.c io.c render_sdl.c tern.c gdb_remote.c \ + m68k.c romdb.c util.c wave.c flac.c blastem.c gen.c mem.c vdp.c ym2612.c \ + ymf262.c ym_common.c vgm.c event_log.c render_audio.c config.c terminal.c \ + z80inst.c menu.c arena.c zlib/adler32.c zlib/compress.c zlib/crc32.c \ + zlib/deflate.c zlib/gzclose.c zlib/gzlib.c zlib/gzread.c zlib/gzwrite.c \ + zlib/infback.c zlib/inffast.c zlib/inflate.c zlib/inftrees.c zlib/trees.c \ + zlib/uncompr.c zlib/zutil.c nuklear_ui/font_android.c \ + nuklear_ui/filechooser_null.c nuklear_ui/blastem_nuklear.c \ + nuklear_ui/sfnt.c ppm.c controller_info.c png.c system.c genesis.c sms.c \ + serialize.c saves.c hash.c xband.c zip.c bindings.c jcart.c paths.c \ + megawifi.c nor.c i2c.c sega_mapper.c realtec.c multi_game.c net.c coleco.c \ + pico_pcm.c ymz263b.c segacd.c lc8951.c cdimage.c cdd_mcu.c cd_graphics.c \ + cdd_fader.c rf5c164.c sft_mapper.c mediaplayer.c oscilloscope.c disasm.c \ + i8255.c gen_player.c LOCAL_SHARED_LIBRARIES := SDL2 diff --git a/Makefile b/Makefile index 0dddc9f..3a9b059 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,28 @@ #disable built-in rules .SUFFIXES : +.PHONY: all clean ifndef OS OS:=$(shell uname -s) endif +ifndef SDL +SDL:=sdl2 +endif +SDL_UPPER:=$(shell echo $(SDL) | tr '[a-z]' '[A-Z]') FIXUP:=true +Z80_DISPATCH:=goto BUNDLED_LIBZ:=zlib/adler32.o zlib/compress.o zlib/crc32.o zlib/deflate.o zlib/gzclose.o zlib/gzlib.o zlib/gzread.o\ zlib/gzwrite.o zlib/infback.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o zlib/trees.o zlib/uncompr.o zlib/zutil.o +UTIL_LDFLAGS:= ifeq ($(OS),Windows) GLEW_PREFIX:=glew MEM:=mem_win.o TERMINAL:=terminal_win.o FONT:=nuklear_ui/font_win.o +CHOOSER:=nuklear_ui/filechooser_win.o NET:=net_win.o EXE:=.exe SO:=dll @@ -32,10 +40,10 @@ GLUDIR:=x64 endif GLEW32S_LIB:=$(GLEW_PREFIX)/lib/Release/$(GLUDIR)/glew32s.lib CFLAGS:=-std=gnu99 -Wreturn-type -Werror=return-type -Werror=implicit-function-declaration -Wpointer-arith -Werror=pointer-arith -LDFLAGS:=-lm -lmingw32 -lws2_32 -mwindows +LDFLAGS:=-lm -lmingw32 -lws2_32 -lcomdlg32 -lole32 -mwindows ifneq ($(MAKECMDGOALS),libblastem.dll) -CFLAGS+= -I"$(SDL2_PREFIX)/include/SDL2" -I"$(GLEW_PREFIX)/include" -DGLEW_STATIC -LDFLAGS+= $(GLEW32S_LIB) -L"$(SDL2_PREFIX)/lib" -lSDL2main -lSDL2 -lopengl32 -lglu32 +CFLAGS+= -I"$(SDL2_PREFIX)/include/$(SDL_UPPER)" -I"$(GLEW_PREFIX)/include" -DGLEW_STATIC +LDFLAGS+= $(GLEW32S_LIB) -L"$(SDL2_PREFIX)/lib" -l$(SDL_UPPER)main -l$(SDL_UPPER) -lopengl32 -lglu32 endif LIBZOBJS=$(BUNDLED_LIBZ) @@ -50,12 +58,21 @@ HAS_PROC:=$(shell if [ -d /proc ]; then /bin/echo -e -DHAS_PROC; fi) CFLAGS:=-std=gnu99 -Wreturn-type -Werror=return-type -Werror=implicit-function-declaration -Wno-unused-value -Wpointer-arith -Werror=pointer-arith $(HAS_PROC) -DHAVE_UNISTD_H ifeq ($(OS),Darwin) -LIBS=sdl2 glew +LIBS=$(SDL) glew FONT:=nuklear_ui/font_mac.o +CHOOSER:=nuklear_ui/filechooser_null.o SO:=dylib else SO:=so +ifeq ($(CPU),wasm) +USE_GLES:=1 +endif + +ifdef USE_RGB565 +CFLAGS+= -DUSE_RGB565 +endif + ifdef USE_FBDEV LIBS=alsa ifndef NOGL @@ -64,13 +81,34 @@ endif CFLAGS+= -DUSE_GLES -DUSE_FBDEV -pthread else ifdef USE_GLES -LIBS=sdl2 glesv2 +LIBS=$(SDL) glesv2 CFLAGS+= -DUSE_GLES else -LIBS=sdl2 glew gl +LIBS=$(SDL) glew gl endif #USE_GLES endif #USE_FBDEV +ifeq ($(CPU),wasm) +CHOOSER:=nuklear_ui/filechooser_null.o +FONT:=nuklear_ui/font_web.o +Z80_DISPATCH:=call +else #CPU=wasm FONT:=nuklear_ui/font.o +ifneq ($(MAKECMDGOALS),libblastem.$(SO)) +CHOOSER:=nuklear_ui/filechooser_gtk.o +GTKFLAGS:=$(shell pkg-config --cflags gtk+-3.0 2>/dev/null) +ifeq ($(GTKFLAGS),) +GTKFLAGS:=$(shell pkg-config --cflags gtk+-2.0 2>/dev/null) +ifeq ($(GTKFLAGS),) +CHOOSER:=nuklear_ui/filechooser_null.o +endif +endif +endif #neq ($(MAKECMDGOALS),libblastem.$(SO)) +endif #CPU=wasm +ifeq ($(GTKFLAGS),) +else +EXTRA_NUKLEAR_LDFLAGS:=-ldl +endif +CFLAGS+= $(GTKFLAGS) endif #Darwin ifdef HOST_ZLIB @@ -84,32 +122,46 @@ ifeq ($(OS),Darwin) #This should really be based on whether or not the C compiler is clang rather than based on the OS CFLAGS+= -Wno-logical-op-parentheses endif + +ifeq ($(CPU),wasm) +CFLAGS+= --use-port=sdl2 +LDFLAGS+= --use-port=sdl2 --embed-file rom.db --embed-file default.cfg --embed-file systems.cfg \ + --embed-file shaders/ --embed-file images/ --embed-file DroidSans.ttf -sEXPORTED_FUNCTIONS=_main,_handle_chooser_result \ + -sEXPORTED_RUNTIME_METHODS=ccall,cwrap,callMain -sINITIAL_HEAP=50331648 --pre-js emscripten_pre.js -lidbfs.js +EXE:=.html +else #CPU=wasm + ifdef PORTABLE ifdef USE_GLES ifndef GLES_LIB GLES_LIB:=$(shell pkg-config --libs glesv2) endif LDFLAGS:=-lm $(GLES_LIB) -else +else #USE_GLES CFLAGS+= -DGLEW_STATIC -Iglew/include -LDFLAGS:=-lm glew/lib/libGLEW.a -endif +LDFLAGS:=-lm glew/lib/libGLEW.a -lEGL +endif #USE_GLES ifeq ($(OS),Darwin) -SDL_INCLUDE_PATH:=Frameworks/SDL2.framework/Headers +SDL_INCLUDE_PATH:=Frameworks/$(SDL_UPPER).framework/Headers CFLAGS+= -mmacosx-version-min=10.10 -LDFLAGS+= -mmacosx-version-min=10.10 -FIXUP:=install_name_tool -change @rpath/SDL2.framework/Versions/A/SDL2 @executable_path/Frameworks/SDL2.framework/Versions/A/SDL2 +LDFLAGS+= -FFrameworks -framework $(SDL_UPPER) -framework OpenGL -framework AppKit -mmacosx-version-min=10.10 +FIXUP:=install_name_tool -change @rpath/$(SDL_UPPER).framework/Versions/A/$(SDL_UPPER) @executable_path/Frameworks/$(SDL_UPPER).framework/Versions/A/$(SDL_UPPER) +else #Darwin +ifeq ($(SDL),sdl3) +SDL_INCLUDE_PATH:=sdl/include/SDL3 +CFLAGS+= -Isdl/include -DSDL_ENABLE_OLD_NAMES else SDL_INCLUDE_PATH:=sdl/include -LDFLAGS+= -Wl,-rpath='$$ORIGIN/lib' -Llib -lSDL2 +endif +LDFLAGS+= -Wl,-rpath='$$ORIGIN/lib' -Llib -l$(SDL_UPPER) ifndef USE_GLES LDFLAGS+= $(shell pkg-config --libs gl) endif endif #Darwin CFLAGS+= -I$(SDL_INCLUDE_PATH) -else +else #PORTABLE ifeq ($(MAKECMDGOALS),libblastem.$(SO)) LDFLAGS:=-lm else @@ -121,25 +173,25 @@ endif endif #libblastem.so ifeq ($(OS),Darwin) -ifneq ($(LIBRETRO),1) LDFLAGS+= -framework OpenGL -framework AppKit endif -endif endif #PORTABLE +endif #CPU=wasm endif #Windows -ifndef OPT ifdef DEBUG +OBJDIR:=obj/debug OPT:=-g3 -O0 else +OBJDIR:=obj/release ifdef NOLTO OPT:=-O2 else OPT:=-O2 -flto endif #NOLTO endif #DEBUG -endif #OPT +LIBOBJDIR:=$(OBJDIR)/lib CFLAGS:=$(OPT) $(CFLAGS) LDFLAGS:=$(OPT) $(LDFLAGS) @@ -178,17 +230,27 @@ ifeq ($(CPU),i386) CPU:=i686 endif endif - -#Haiku uses a different name for 32bit -ifeq ($(CPU),BePC) -CPU:=i686 +ifeq ($(CPU),x86_64) +CFLAGS+=-DX86_64 -m64 +LDFLAGS+=-m64 +else +ifeq ($(CPU),i686) +CFLAGS+=-DX86_32 -m32 +LDFLAGS+=-m32 +else +NEW_CORE:=1 +endif endif TRANSOBJS=gen.o backend.o $(MEM) arena.o tern.o -M68KOBJS=68kinst.o +M68KOBJS=68kinst.o disasm.o + +ifdef NO_FILE_CHOOSER +CHOOSER:=nuklear_ui/filechooser_nulll.o +endif ifdef NEW_CORE -Z80OBJS=z80.o z80inst.o +Z80OBJS=z80.o z80inst.o M68KOBJS+= m68k.o CFLAGS+= -DNEW_CORE else @@ -203,53 +265,44 @@ TRANSOBJS+= gen_x86.o backend_x86.o endif endif endif -AUDIOOBJS=ym2612.o psg.o wave.o vgm.o event_log.o render_audio.o -CONFIGOBJS=config.o tern.o util.o paths.o -NUKLEAROBJS=$(FONT) nuklear_ui/blastem_nuklear.o nuklear_ui/sfnt.o +AUDIOOBJS=ym2612.o ymf262.o ym_common.o psg.o wave.o flac.o vgm.o event_log.o render_audio.o rf5c164.o +CONFIGOBJS=config.o tern.o util.o paths.o +NUKLEAROBJS=$(FONT) $(CHOOSER) nuklear_ui/blastem_nuklear.o nuklear_ui/sfnt.o nuklear_ui/debug_ui.o RENDEROBJS=ppm.o controller_info.o ifdef USE_FBDEV RENDEROBJS+= render_fbdev.o else RENDEROBJS+= render_sdl.o endif - + ifdef NOZLIB CFLAGS+= -DDISABLE_ZLIB else RENDEROBJS+= $(LIBZOBJS) png.o endif -MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o $(RENDEROBJS) io.o romdb.o hash.o menu.o xband.o \ - realtec.o i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ - $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o - -LIBOBJS=libblastem.o system.o genesis.o debug.o gdb_remote.o vdp.o io.o romdb.o hash.o xband.o realtec.o \ - i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ - $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o rom.db.o gen_player.o $(LIBZOBJS) - -ifdef NONUKLEAR -CFLAGS+= -DDISABLE_NUKLEAR -else -MAINOBJS+= $(NUKLEAROBJS) -endif - -ifeq ($(CPU),x86_64) -CFLAGS+=-DX86_64 -m64 -LDFLAGS+=-m64 -else -ifeq ($(CPU),i686) -CFLAGS+=-DX86_32 -m32 -LDFLAGS+=-m32 -else -$(error $(CPU) is not a supported architecture) -endif -endif +COREOBJS:=system.o genesis.o vdp.o io.o romdb.o hash.o xband.o realtec.o i2c.o nor.o $(M68KOBJS) \ + sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ + $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o gen_player.o coleco.o pico_pcm.o ymz263b.o \ + segacd.o lc8951.o cdimage.o cdd_mcu.o cd_graphics.o cdd_fader.o sft_mapper.o mediaplayer.o \ + laseractive.o upd78k2_dis.o upd78k2.o osd_font.o pd0178.o radica.o 32x.o 32x_video.o sh2.o \ + sh2_decode.o sh7095.o ifdef NOZ80 CFLAGS+=-DNO_Z80 else -MAINOBJS+= sms.o $(Z80OBJS) -LIBOBJS+= sms.o $(Z80OBJS) +COREOBJS+= sms.o i8255.o korean_sms_multi.o $(Z80OBJS) +endif + +MAINOBJS:=$(COREOBJS) blastem.o $(RENDEROBJS) zip.o menu.o debug.o gdb_remote.o bindings.o oscilloscope.o + +LIBOBJS:=$(COREOBJS) libblastem.o rom.db.o $(LIBZOBJS) + +ifdef NONUKLEAR +CFLAGS+= -DDISABLE_NUKLEAR +else +MAINOBJS+= $(NUKLEAROBJS) +LDFLAGS+=$(EXTRA_NUKLEAR_LDFLAGS) endif ifeq ($(OS),Windows) @@ -268,105 +321,138 @@ ifdef FONT_PATH CFLAGS+= -DFONT_PATH='"'$(FONT_PATH)'"' endif -ALL=dis$(EXE) zdis$(EXE) vgmplay$(EXE) blastem$(EXE) +ALL=dis$(EXE) zdis$(EXE) upddis$(EXE) sh2dis$(EXE) blastem$(EXE) ifneq ($(OS),Windows) ALL+= termhelper endif +DISOBJS:=dis.o disasm.o backend.o 68kinst.o tern.o vos_program_module.o util.o +ZDISOBJS:=zdis.o z80inst.o tern.o util.o +MTESTOBJS:=trans.o serialize.o $(M68KOBJS) $(TRANSOBJS) util.o +ZTESTOBJS:=ztestrun.o serialize.o $(Z80OBJS) $(TRANSOBJS) util.o +CPMOBJS:=blastcpm.o util.o serialize.o $(Z80OBJS) $(TRANSOBJS) +UPD78K2RUNOBJS:=upd78k2.o upd78k2run.o util.o backend.o tern.o +UPDDISOBJS:=upddis.o upd78k2_dis.o disasm.o tern.o util.o backend.o +SH2DISOBJS:=sh2dis.o sh2_decode.o disasm.o tern.o util.o backend.o -ifeq ($(MAKECMDGOALS),libblastem.$(SO)) -CFLAGS+= -fpic -DIS_LIB -endif +LIBCFLAGS=$(CFLAGS) -fpic -DIS_LIB -DDISABLE_ZLIB all : $(ALL) -libblastem.$(SO) : $(LIBOBJS) +ORDERONLY:=$(OBJDIR) +LIBORDERONLY:=$(LIBOBJDIR) +ifdef NEW_CORE +ifeq ($(wildcard $(OBJDIR)/*.d),) +ORDERONLY+= m68k.c z80.c +endif +ifeq ($(wildcard $(LIBOBJDIR)/*.d),) +LIBORDERONLY+= m68k.c z80.c +endif +endif + +#if we've never built the uPD78K2 core before we don't know which other files +#depend on its header, so force DSL compilation to occur before everything else +ifeq ($(wildcard $(OBJDIR)/upd78k2.d),) +ORDERONLY+= upd78k2.c +endif +ifeq ($(wildcard $(LIBOBJDIR)/upd78k2.d),) +LIBORDERONLY+= upd78k2.c +endif +#same for the SH2 core +ifeq ($(wildcard $(OBJDIR)/sh2.d),) +ORDERONLY+= sh2.c +endif +ifeq ($(wildcard $(LIBOBJDIR)/sh2.d),) +LIBORDERONLY+= sh2.c +endif + +-include $(MAINOBJS:%.o=$(OBJDIR)/%.d) +-include $(LIBOBJS:%.o=$(LIBOBJDIR)/%.d) +-include $(DISOBJS:%.o=$(OBJDIR)/%.d) +-include $(ZDISOBJS:%.o=$(OBJDIR)/%.d) +-include $(UPD78K2RUNOBJS:%.o=$(OBJDIR)/%.d) +-include $(UPDDISOBJS:%.o=$(OBJDIR)/%.d) +-include $(SH2DISOBJS:%.o=$(OBJDIR)/%.d) +-include $(OBJDIR)/trans.d +-include $(OBJDIR)/ztestrun.d +-include $(OBJDIR)/blastcpm.d + +$(OBJDIR) : + mkdir -p $(OBJDIR)/nuklear_ui + mkdir -p $(OBJDIR)/zlib + +$(LIBOBJDIR) : + mkdir -p $(LIBOBJDIR)/zlib + +libblastem.$(SO) : $(LIBOBJS:%.o=$(LIBOBJDIR)/%.o) $(CC) -shared -o $@ $^ $(LDFLAGS) -blastem$(EXE) : $(MAINOBJS) +blastem$(EXE) : $(MAINOBJS:%.o=$(OBJDIR)/%.o) $(CC) -o $@ $^ $(LDFLAGS) $(PROFFLAGS) $(FIXUP) ./$@ - -blastjag$(EXE) : jaguar.o jag_video.o $(RENDEROBJS) serialize.o $(M68KOBJS) $(TRANSOBJS) $(CONFIGOBJS) + +termhelper : $(OBJDIR)/termhelper.o $(CC) -o $@ $^ $(LDFLAGS) -termhelper : termhelper.o - $(CC) -o $@ $^ $(LDFLAGS) - -dis$(EXE) : dis.o 68kinst.o tern.o vos_program_module.o - $(CC) -o $@ $^ $(OPT) - -jagdis : jagdis.o jagcpu.o tern.o - $(CC) -o $@ $^ - -zdis$(EXE) : zdis.o z80inst.o - $(CC) -o $@ $^ - -libemu68k.a : $(M68KOBJS) $(TRANSOBJS) - ar rcs libemu68k.a $(M68KOBJS) $(TRANSOBJS) - -trans : trans.o serialize.o $(M68KOBJS) $(TRANSOBJS) util.o +dis$(EXE) : $(DISOBJS:%.o=$(OBJDIR)/%.o) $(CC) -o $@ $^ $(OPT) -transz80 : transz80.o $(Z80OBJS) $(TRANSOBJS) - $(CC) -o transz80 transz80.o $(Z80OBJS) $(TRANSOBJS) +jagdis : $(OBJDIR)/jagdis.o $(OBJDIR)/jagcpu.o $(OBJDIR)/tern.o + $(CC) -o $@ $^ $(OPT) -ztestrun : ztestrun.o serialize.o $(Z80OBJS) $(TRANSOBJS) - $(CC) -o ztestrun $^ $(OPT) +zdis$(EXE) : $(ZDISOBJS:%.o=$(OBJDIR)/%.o) + $(CC) -o $@ $^ $(OPT) -ztestgen : ztestgen.o z80inst.o - $(CC) -ggdb -o ztestgen ztestgen.o z80inst.o +trans : $(MTESTOBJS:%.o=$(OBJDIR)/%.o) + $(CC) -o $@ $^ $(OPT) -vgmplay$(EXE) : vgmplay.o $(RENDEROBJS) serialize.o $(CONFIGOBJS) $(AUDIOOBJS) - $(CC) -o $@ $^ $(LDFLAGS) - $(FIXUP) ./$@ +ztestrun : $(ZTESTOBJS:%.o=$(OBJDIR)/%.o) + $(CC) -o $@ $^ $(OPT) -blastcpm : blastcpm.o util.o serialize.o $(Z80OBJS) $(TRANSOBJS) +ztestgen : $(OBJDIR)/ztestgen.o $(OBJDIR)/z80inst.o + $(CC) -o $@ $^ $(OPT) + +blastcpm$(EXE) : $(CPMOBJS:%.o=$(OBJDIR)/%.o) $(CC) -o $@ $^ $(OPT) $(PROFFLAGS) -test : test.o vdp.o - $(CC) -o test test.o vdp.o +vos_prog_info : $(OBJDIR)/vos_prog_info.o $(OBJDIR)/vos_program_module.o + $(CC) -o $@ $^ $(OPT) -testgst : testgst.o gst.o - $(CC) -o testgst testgst.o gst.o +upd78k2run : $(UPD78K2RUNOBJS:%.o=$(OBJDIR)/%.o) + $(CC) -o $@ $^ $(OPT) -test_x86 : test_x86.o gen_x86.o gen.o - $(CC) -o test_x86 test_x86.o gen_x86.o gen.o +upddis$(EXE) : $(UPDDISOBJS:%.o=$(OBJDIR)/%.o) + $(CC) -o $@ $^ $(OPT) -test_arm : test_arm.o gen_arm.o mem.o gen.o - $(CC) -o test_arm test_arm.o gen_arm.o mem.o gen.o - -test_int_timing : test_int_timing.o vdp.o - $(CC) -o $@ $^ +sh2dis$(EXE) : $(SH2DISOBJS:%.o=$(OBJDIR)/%.o) + $(CC) -o $@ $^ $(OPT) -gen_fib : gen_fib.o gen_x86.o mem.o - $(CC) -o gen_fib gen_fib.o gen_x86.o mem.o - -offsets : offsets.c z80_to_x86.h m68k_core.h - $(CC) -o offsets offsets.c - -vos_prog_info : vos_prog_info.o vos_program_module.o - $(CC) -o vos_prog_info vos_prog_info.o vos_program_module.o - -m68k.c : m68k.cpu cpu_dsl.py - ./cpu_dsl.py -d call $< > $@ - -%.c : %.cpu cpu_dsl.py - ./cpu_dsl.py -d goto $< > $@ +.PRECIOUS: %.c +%.c %.h : %.cpu cpu_dsl.py + ./cpu_dsl.py -d $(shell echo $@ | sed -E -e "s/^z80.*$$/$(Z80_DISPATCH)/" -e '/^goto/! s/^.*$$/call/') $< > $(shell echo $@ | sed -E 's/\.[ch]$$/./')c %.db.c : %.db - sed -e 's/"/\\"/g' -e 's/^\(.*\)$$/"\1\\n"/' -e'1s/^\(.*\)$$/const char $(shell echo $< | tr '.' '_')_data[] = \1/' -e '$$s/^\(.*\)$$/\1;/' $< > $@ + sed $< -e 's/"/\\"/g' -e 's/^\(.*\)$$/"\1\\n"/' -e'1s/^\(.*\)$$/const char $(shell echo $< | tr '.' '_')_data[] = \1/' -e '$$s/^\(.*\)$$/\1;/' > $@ -%.o : %.S - $(CC) -c -o $@ $< +$(OBJDIR)/%.o : %.S | $(ORDERONLY) + $(CC) -c -MMD -o $@ $< -%.o : %.c - $(CC) $(CFLAGS) -c -o $@ $< - -%.o : %.m - $(CC) $(CFLAGS) -c -o $@ $< +$(OBJDIR)/%.o : %.c | $(ORDERONLY) + $(CC) $(CFLAGS) -c -MMD -o $@ $< + +$(OBJDIR)/%.o : %.m | $(ORDERONLY) + $(CC) $(CFLAGS) -c -MMD -o $@ $< + +$(LIBOBJDIR)/%.o : %.S | $(LIBORDERONLY) + $(CC) -c -MMD -o $@ $< + +$(LIBOBJDIR)/%.o : %.c | $(LIBORDERONLY) + $(CC) $(LIBCFLAGS) -c -MMD -o $@ $< + +$(LIBOBJDIR)/%.o : %.m | $(LIBORDERONLY) + $(CC) $(LIBCFLAGS) -c -MMD -o $@ $< %.png : %.xcf - xcf2png $< > $@ + convert -background none -flatten $< $@ %.tiles : %.spec ./img2tiles.py -s $< $@ @@ -379,8 +465,8 @@ m68k.c : m68k.cpu cpu_dsl.py %.bin : %.sz8 vasmz80_mot -Fbin -spaces -o $@ $< -res.o : blastem.rc - $(WINDRES) blastem.rc res.o +$(OBJDIR)/res.o : blastem.rc + $(WINDRES) $< $@ arrow.tiles : arrow.png cursor.tiles : cursor.png @@ -392,4 +478,4 @@ menu.bin : font_interlace_variable.tiles arrow.tiles cursor.tiles button.tiles f tmss.md : font.tiles clean : - rm -rf $(ALL) trans ztestrun ztestgen *.o nuklear_ui/*.o zlib/*.o + rm -rf $(ALL) trans ztestrun ztestgen *.o nuklear_ui/*.o zlib/*.o $(OBJDIR) diff --git a/README b/README index fce3ea3..390e30b 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -BlastEm 0.6.0 +BlastEm 0.6.X ------------- Installation @@ -9,7 +9,7 @@ Extract this archive to a directory of your choosing. NOTE: Prior to version 0.4.1, BlastEm was still using Unixy locations for config and save files. If you're upgrading from a previous version on Windows, you will need to move them manually. For config files, the relevant paths are in the -previous paragraph. For save files, move all the directories found in +previous paragraph. For save files, move all the directories found in %userprofile%\.local\share\blastem to %localappdata%\blastem Usage @@ -17,23 +17,22 @@ Usage This version of BlastEm has a GUI that allows access to most configuration options. Simply start BlastEm without passing a ROM filename on the command line to access -the main menu. You can also access the menu by hitting the button mapped to the ui.exit +the main menu. You can also access the menu by hitting the button mapped to the ui.menu action (default Esc). -If Open GL is disabled or unavaible, or you explicitly request it, the old ROM-based UI -will be used instead. This UI does not support configuration so you will need to modify -the configuration file manually if you use it. See the rest of this README for instructions -on modifying the configuration file. +If you explicitly request it, you can still use the old ROM-based UI instead. This UI does +not support configuration so you will need to modify the configuration file manually if you +use it. See the rest of this README for instructions on modifying the configuration file. Some operations are currently only supported through the command line. To get a list of supported command line options on Linux or OSX type: ./blastem -h - + From within your BlastEm directory. On Windows type: - + blastem.exe -h - + Lock-On Support --------------- @@ -46,7 +45,7 @@ the command line, specify the Sonic & Knuckles ROM as the primary ROM and specify the ROM to be locked on using the -o option. As an example: ./blastem ~/romz/sonic_and_knuckles.bin -o ~/romz/sonic3.bin - + Please note that Sonic 2 lock-on does not work at this time. Configuration @@ -55,7 +54,7 @@ Configuration Configuration is read from the file at $HOME/.config/blastem/blastem.cfg on Unix-like systems and %localappdata%\blastem\blastem.cfg if it exists. Othwerise it is read from default.cfg from the same directory as the BlastEm -executable. Sections are denoted by a section name followed by an open curly +executable. Sections are denoted by a section name followed by an open curly bracket, the section's contents and a closing curly bracket. Individual configuration values are set by entering the value's name followed by a space or tab and followed by the desired value. @@ -105,10 +104,26 @@ don't correspond to a normal character, check the list below: play search back + np0 Number Pad 0 + np1 Number Pad 1 + np2 Number Pad 2 + np3 Number Pad 3 + np4 Number Pad 4 + np5 Number Pad 5 + np6 Number Pad 6 + np7 Number Pad 7 + np8 Number Pad 8 + np9 Number Pad 9 + np/ Number Pad / + np* Number Pad * + np- Number Pad - + np+ Number Pad + + npenter Number Pad Enter + np. Number Pad . The pads subsection is used to map gamepads and joysticks. Gamepads that are -recognized, can have their buttons and axes mapped with semantic names. -Xbox 360, PS4 and PS3 style names are supported. Unrecognized gamepads can be +recognized, can have their buttons and axes mapped with semantic names. +Xbox 360, PS4 and PS3 style names are supported. Unrecognized gamepads can be mapped using numeric button and axis ids. The following button names are recognized by BlastEm: a, cross @@ -129,7 +144,7 @@ The following axis names are recognized by BlastEm: righty lefttrigger, l2 righttrigger, r2 - + The mice subsection is used to map mice to emulated Mega/Sega mice. The default configuration maps both the first and second host mice to the first emulated @@ -153,19 +168,27 @@ ui.plane_debug Toggles the VDP plane debug view ui.vram_debug Toggles the VDP VRAM debug view ui.cram_debug Toggles the VDP CRAM debug view ui.compositing_debug Toggles the VDP compositing debug view +ui.oscilloscope Toggles an oscilloscope visualization +ui.cd_graphics_debug Toggles the Sega CD graphics ASIC debug view ui.vdp_debug_mode Cycles the mode/palette of the VDP debug view that currently has focus ui.enter_debugger Enters the debugger for the main CPU of the currently emulated system ui.screenshot Takes an internal screenshot -ui.exit Returns to the menu ROM if currently in a game - that was launched from the menu. Exits otherwise +ui.record_video Toggles video recording. Videos are recorded as an + animated PNG paired with a WAV file. +ui.vgm_log Toggles VGM file logging +ui.menu Opens the in-game menu, or exits the menu if it + is currently open. +ui.exit Quits the emulator ui.save_state Saves a savestate to the quicksave slot ui.set_speed.N Selects a specific machine speed specified by N which should be a number between 0-9. Speeds are - specified in the "clocks" section of the config + specified in the "clocks" section of the config ui.next_speed Selects the next machine speed ui.prev_speed Selects the previous machine speed +ui.pause Pauses emulation +ui.advance Advances a single frame if paused. Pauses if not. ui.toggle_fullscreen Toggles between fullscreen and windowed mode ui.soft_reset Resets a portion of the emulated machine Equivalent to pushing the reset button on the @@ -176,7 +199,23 @@ ui.sms_pause Triggers a press of the pause button when in SMS mode ui.toggle_keyboard_captured Toggles the capture state of the host keyboard when an emulated keyboard is present - +ui.release_mouse Release the mouse from the captured state +ui.paste Pastes the contents of the clipboard into the + emulated system by simulating keypresses. + Currently, this is only implemented for SC-3000 + +Cassette Actions +---------------- + +When emulating the SC-3000, cassette emulation is supported via the lock-on +functionality. The following binding names can be triggered by a key or gamepad +binding. + +cassette.play Start playing the virtual tape +cassette.record Starting recording on the virtual tape +cassette.stop Stop the virtual tape at the current position +cassette.rewind Rewind the virtual tape + IO -- @@ -185,7 +224,22 @@ IO assignments can be overridden by the ROM database when appropriate. For instance, games with mouse support can automatically use the mouse and games that only support 3-button pads can automatically force an appropriate pad. Unforunately, the ROM database is not yet exhaustive so manual configuration -may be needed here in some cases. +may be needed here in some cases. The following IO devices are recognized + +gamepad.2 SMS gamepad +gamepad.3 3-button Gen/MD gamepad +gamepad.6 6-button Gen/MD gamepad +heartbeat_trainer Special peripheral used for the Heartbeat Personal Trainer +mouse Sega/Mega mouse +saturn_keyboard Saturn keybord or keyboard adapter +xband_keyboard Xband keyboard +sega_parallel PC Parallel port connection used by some of Sega's devtools +generic Generic pipe-based device emulation for use with external + software. +serial Generic pipe-based serial emulation +sega_multitap Sega's 4-player Multitap +ea_multitap_port_a First port of EA's 4-way Play multitap +ea_multitap_port_a Second port of EA's 4-way Play multitap Video ----- @@ -203,7 +257,7 @@ left unspecified, it will be calculated from "width" and "aspect". "vertex_shader" and "fragment_shader" define the GLSL shader program that produces the final image for each frame. Shaders can be used to add various visual effects or enhancements. Currently BlastEm only ships with the default -shader and a "subtle" crt shader. If you write your own shaders, place them in +shader and a "subtle" crt shader. If you write your own shaders, place them in $HOME/.config/blastem/shaders and then specify the basename of the new shader files in the "vertex_shader" and "fragment_shader" config options. Note that shaders are not available in the SDL fallback renderer. @@ -334,7 +388,7 @@ defined in the OS environment can be used. "remember_path" specifies whether BlastEm should remember the last path used in the file browser. When it is set to "on", the last path will be remembered and -used instead of "initial_path" in subsequent runs. If it is set to "off", +used instead of "initial_path" in subsequent runs. If it is set to "off", "initial_path" will always be used. "screenshot_path" specifies the directory "internal" screenshots will be saved @@ -364,7 +418,7 @@ This section explains the meaning of the special path variables referenced in the previous section. $HOME The home directory of the current user. On most Unix variants, it - will be a subdirectory of /home. On Windows it will typically be a + will be a subdirectory of /home. On Windows it will typically be a subdirectory of C:\Users $EXEDIR The directory the BlastEm executable is located in $USERDATA This is an OS-specific path used for storing application specific @@ -401,43 +455,77 @@ Debugger -------- BlastEm has an integrated command-line debugger loosely based on GDB's -interface. The interface is very rough at the moment. Available commands in the -68K debugger are: - b ADDRESS - Set a breakpoint at ADDRESS - d BREAKPOINT - Delete a 68K breakpoint - co BREAKPOINT - Run a list of debugger commands each time - BREAKPOINT is hit - a ADDRESS - Advance to address - n - Advance to next instruction - o - Advance to next instruction ignoring branches to - lower addresses (good for breaking out of loops) - s - Advance to next instruction (follows bsr/jsr) - c - Continue - bt - Print a backtrace - p[/(x|X|d|c)] VALUE - Print a register or memory location - di[/(x|X|d|c)] VALUE - Print a register or memory location each time - a breakpoint is hit - vs - Print VDP sprite list - vr - Print VDP register info - zb ADDRESS - Set a Z80 breakpoint - zp[/(x|X|d|c)] VALUE - Display a Z80 value - q - Quit BlastEm -Available commands in the Z80 debugger are: - b ADDRESS - Set a breakpoint at ADDRESS - de BREAKPOINT - Delete a Z80 breakpoint - a ADDRESS - Advance to address - n - Advance to next instruction - c - Continue - p[/(x|X|d|c)] VALUE - Print a register or memory location - di[/(x|X|d|c)] VALUE - Print a register or memory location each time - a breakpoint is hit - q - Quit BlastEm +interface. It contains a turing complete language to support complex +breakpoint conditions and to allow some basic scripting. The -d flag can be used to cause BlastEm to start in the debugger. Alternatively, you can use the ui.enter_debugger action (mapped to the 'u' key by default) to enter the debugger while a game is running. To debug the menu ROM, use the -dm flag. + +Top level debugger commands take the following form: + + COMMAND[/FORMAT_CHAR] [ARGS] + +Where COMMAND is the name of the command, an unambiguous prefix or an explict +alias. FORMAT_CHAR is a single character specifying the desired format for +certain commands. ARGS is a list of arguments separated by spaces. Arguments +must be syntactically valid as an expression and are generally evaluated as +such, but there are some exceptions. For instance, the "function" command +expects its first argument to be the bare name of the function. This is used +directly rather than evaluated. + +To get a list of commands valid in the current context use the help command. +The help command will also list explicit aliases for a command and its expected +arguments. + +The commands available in all contexts are: + quit + help + breakpoint ADDRESS Add a new breakpoint at ADDRESS for the + current CPU + watchpoint ADDRESS [SIZE] Set a watchpoint at ADDRESS with an optional + size in bytes. If not specified, the default + size is context dependent. + continue Resume execution + advance ADDRESS Resume execution until ADDRESS is reached + step Advance to the next instruction, stepping + into subroutines. + next Advance to the next instruction, stepping + over subroutine calls. + over Similar to next, but ignores branches to + a lower address. Good for skipping over + loops. + backtrace Attempts to print a backtrace. Note that + this uses heuristics and will not provide + correct results in all cases. + frames EXPRESSION Resume execution for EXPRESSION video frames + print[/FORMAT] EXPRESSION Print the value of EXPRESSION using FORMAT + printf FORMAT EXPRESSION... Basically the same as the C function + softreset Equivalent to the ui.soft_reset binding + display[/FORMAT] EXPRESSION... Print one ore more expressions every time + this debug context is entered + deletedisplay Removes an entry added via "display" + commands BREAKPOINT Set a list of commands to execute when a + breakpoint is hit. End the list with "end" + function NAME [ARGS...] Creates a user-defined named NAME with + arguments ARGS. Enter the body of the + function on subsequent lines. End with "end" + return VALUE Returns a value inside a function + set MEM|NAME VALUE Sets a register, symbol or memory location + to VALUE + variable NAME [VALUE] Create a new variable called NAME and set it + to VALUE or 0 if no value provided + array NAME [VALUE...] Create a new array called NAME if it doesn't + already exist. The array is initialized with + the remaining parameters. + append NAME VALUE Appens VALUE to the array NAME + bindup NAME Simulate a keyup for binding NAME + binddown NAME Simulate a keydown for binding NAME + +TODO: expressions, builtin functions and operators + GDB Remote Debugging -------------------- @@ -453,7 +541,7 @@ installation and ROM_FILE.bin is the name of the raw binary for your program. BlastEm will halt at the beginning of your program's entry point and return control to GDB. This will allow you to set breakpoints before your code runs. -On Windows, the procedure is slightly different. First run +On Windows, the procedure is slightly different. First run blastem.exe ROM_FILE.bin -D This will cause BlastEm to wait for a socket connection on port 1234. It will appear to be frozen until gdb connects to it. Now open the ELF file in gdb @@ -468,12 +556,10 @@ Included Tools BlastEm ships with a few small utilities that leverage portions of the emulator code. - + dis - 68K disassembler zdis - Z80 disassembler - vgmplay - Very basic VGM player - stateview - GST save state viewer - + Sync Source and VSync ----- @@ -509,9 +595,9 @@ Charles MacDonald - While it hasn't been updated in a while, I still find his hardware document has also come in handy. Eke-Eke - Eke-Eke wrote a great document on the use of I2C EEPROM in - Genesis games and also left some useful very helpful + Genesis games and also left some useful very helpful comments about problematic games in Genesis Plus GX - + Sauraen - Sauraen has analyzed the YM2203 and YM2612 dies and written a VHDL operator implementation. These have been useful in improving the accuracy of my YM2612 core. @@ -524,16 +610,16 @@ Alexey Khokholov - Alexey (aka Nuke.YKT) has analyzed the YM3438 die and writt Bart Trzynadlowski - His documents on the Genecyst save-state format and the mapper used in Super Street Fighter 2 were definitely appreciated. - + KanedaFR - Kaneda's SpritesMind forum is a great resource for the Sega development community. - + Titan - Titan has created what are without a doubt the most impressive demos on the Megadrive. Additionally, I am very grateful for the documentation provided by Kabuto and the assistance of Kabuto, Sik and Jorge in getting Overdrive 2 to run properly in BlastEm. - + flamewing - flamewing created a very handy exhaustive test ROM for 68K BCD instructions and documented the proper behavior for certain BCD edge cases @@ -557,4 +643,4 @@ COPYING for full license details. Binary releases of BlastEm are packaged with GLEW, SDL2 and zlib which have their own licenses. See GLEW-LICENSE and SDL-LICENSE for details. For zlib license -information, please see zlib.h in the source code release. +information, please see zlib/zlib.h or zlib/LICENSE in the source code release. diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml deleted file mode 100644 index ac123c7..0000000 --- a/android/AndroidManifest.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/ant.properties b/android/ant.properties deleted file mode 100644 index b0971e8..0000000 --- a/android/ant.properties +++ /dev/null @@ -1,17 +0,0 @@ -# This file is used to override default values used by the Ant build system. -# -# This file must be checked into Version Control Systems, as it is -# integral to the build system of your project. - -# This file is only used by the Ant script. - -# You can use this to override default values such as -# 'source.dir' for the location of your java source folder and -# 'out.dir' for the location of your output folder. - -# You can also use it define how the release builds are signed by declaring -# the following properties: -# 'key.store' for the location of your keystore and -# 'key.alias' for the name of the key to use. -# The password will be asked during the build when you use the 'release' target. - diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..f0ce2eb --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,75 @@ +def buildAsLibrary = project.hasProperty('BUILD_AS_LIBRARY'); +def buildAsApplication = !buildAsLibrary +if (buildAsApplication) { + apply plugin: 'com.android.application' +} +else { + apply plugin: 'com.android.library' +} + +android { + if (buildAsApplication) { + namespace "com.retrodev.blastem" + } + compileSdkVersion 35 + defaultConfig { + minSdkVersion 19 + targetSdkVersion 35 + versionCode 1 + versionName "1.0" + externalNativeBuild { + ndkBuild { + arguments "APP_PLATFORM=android-19" + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + } + // cmake { + // arguments "-DANDROID_APP_PLATFORM=android-19", "-DANDROID_STL=c++_static" + // // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + // abiFilters 'arm64-v8a' + // } + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + applicationVariants.all { variant -> + tasks["merge${variant.name.capitalize()}Assets"] + .dependsOn("externalNativeBuild${variant.name.capitalize()}") + } + if (!project.hasProperty('EXCLUDE_NATIVE_LIBS')) { + sourceSets.main { + jniLibs.srcDir 'libs' + } + externalNativeBuild { + ndkBuild { + path 'jni/Android.mk' + } + // cmake { + // path 'jni/CMakeLists.txt' + // } + } + + } + lint { + abortOnError false + } + + if (buildAsLibrary) { + libraryVariants.all { variant -> + variant.outputs.each { output -> + def outputFile = output.outputFile + if (outputFile != null && outputFile.name.endsWith(".aar")) { + def fileName = "org.libsdl.app.aar"; + output.outputFile = new File(outputFile.parent, fileName); + } + } + } + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') +} diff --git a/android/jni/Android.mk b/android/app/jni/Android.mk similarity index 75% rename from android/jni/Android.mk rename to android/app/jni/Android.mk index 8e68f8d..5053e7d 100644 --- a/android/jni/Android.mk +++ b/android/app/jni/Android.mk @@ -1,2 +1 @@ -APP_ABI=x86 include $(call all-subdir-makefiles) diff --git a/android/app/jni/Application.mk b/android/app/jni/Application.mk new file mode 100644 index 0000000..12bad51 --- /dev/null +++ b/android/app/jni/Application.mk @@ -0,0 +1,10 @@ + +# Uncomment this if you're using STL in your project +# You can find more information here: +# https://developer.android.com/ndk/guides/cpp-support +# APP_STL := c++_shared + +APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 + +# Min runtime API level +APP_PLATFORM=android-19 diff --git a/android/app/jni/src b/android/app/jni/src new file mode 120000 index 0000000..a8a4f8c --- /dev/null +++ b/android/app/jni/src @@ -0,0 +1 @@ +../../.. \ No newline at end of file diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000..a4c9886 --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,98 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in [sdk]/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLInputConnection { + void nativeCommitText(java.lang.String, int); + void nativeGenerateScancodeForUnichar(char); +} + +-keep,includedescriptorclasses class org.libsdl.app.SDLActivity { + # for some reason these aren't compatible with allowoptimization modifier + boolean supportsRelativeMouse(); + void setWindowStyle(boolean); +} + +-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLActivity { + java.lang.String nativeGetHint(java.lang.String); # Java-side doesn't use this, so it gets minified, but C-side still tries to register it + boolean onNativeSoftReturnKey(); + void onNativeKeyboardFocusLost(); + boolean isScreenKeyboardShown(); + android.util.DisplayMetrics getDisplayDPI(); + java.lang.String clipboardGetText(); + boolean clipboardHasText(); + void clipboardSetText(java.lang.String); + int createCustomCursor(int[], int, int, int, int); + void destroyCustomCursor(int); + android.content.Context getContext(); + boolean getManifestEnvironmentVariables(); + android.view.Surface getNativeSurface(); + void initTouch(); + boolean isAndroidTV(); + boolean isChromebook(); + boolean isDeXMode(); + boolean isTablet(); + void manualBackButton(); + int messageboxShowMessageBox(int, java.lang.String, java.lang.String, int[], int[], java.lang.String[], int[]); + void minimizeWindow(); + int openURL(java.lang.String); + void requestPermission(java.lang.String, int); + int showToast(java.lang.String, int, int, int, int); + boolean sendMessage(int, int); + boolean setActivityTitle(java.lang.String); + boolean setCustomCursor(int); + void setOrientation(int, int, boolean, java.lang.String); + boolean setRelativeMouseEnabled(boolean); + boolean setSystemCursor(int); + boolean shouldMinimizeOnFocusLoss(); + boolean showTextInput(int, int, int, int); +} + +-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.HIDDeviceManager { + boolean initialize(boolean, boolean); + boolean openDevice(int); + int sendOutputReport(int, byte[]); + int sendFeatureReport(int, byte[]); + boolean getFeatureReport(int, byte[]); + void closeDevice(int); +} + +-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLAudioManager { + int[] getAudioOutputDevices(); + int[] getAudioInputDevices(); + int[] audioOpen(int, int, int, int, int); + void audioWriteFloatBuffer(float[]); + void audioWriteShortBuffer(short[]); + void audioWriteByteBuffer(byte[]); + void audioClose(); + int[] captureOpen(int, int, int, int, int); + int captureReadFloatBuffer(float[], boolean); + int captureReadShortBuffer(short[], boolean); + int captureReadByteBuffer(byte[], boolean); + void captureClose(); + void audioSetThreadPriority(boolean, int); + native int nativeSetupJNI(); + native void removeAudioDevice(boolean, int); + native void addAudioDevice(boolean, int); +} + +-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLControllerManager { + void pollInputDevices(); + void pollHapticDevices(); + void hapticRun(int, float, int); + void hapticStop(int); +} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..316b4a3 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/assets/default.cfg b/android/app/src/main/assets/default.cfg new file mode 120000 index 0000000..c19ac97 --- /dev/null +++ b/android/app/src/main/assets/default.cfg @@ -0,0 +1 @@ +../../../../../default.cfg \ No newline at end of file diff --git a/android/app/src/main/assets/images b/android/app/src/main/assets/images new file mode 120000 index 0000000..c5c9cab --- /dev/null +++ b/android/app/src/main/assets/images @@ -0,0 +1 @@ +../../../../../images \ No newline at end of file diff --git a/android/app/src/main/assets/rom.db b/android/app/src/main/assets/rom.db new file mode 120000 index 0000000..43aa008 --- /dev/null +++ b/android/app/src/main/assets/rom.db @@ -0,0 +1 @@ +../../../../../rom.db \ No newline at end of file diff --git a/android/app/src/main/assets/shaders b/android/app/src/main/assets/shaders new file mode 120000 index 0000000..0740be4 --- /dev/null +++ b/android/app/src/main/assets/shaders @@ -0,0 +1 @@ +../../../../../shaders \ No newline at end of file diff --git a/android/app/src/main/assets/systems.cfg b/android/app/src/main/assets/systems.cfg new file mode 120000 index 0000000..29c1b02 --- /dev/null +++ b/android/app/src/main/assets/systems.cfg @@ -0,0 +1 @@ +../../../../../systems.cfg \ No newline at end of file diff --git a/android/app/src/main/java/com/retrodev/blastem/BlastEmActivity.java b/android/app/src/main/java/com/retrodev/blastem/BlastEmActivity.java new file mode 100644 index 0000000..0a34eb6 --- /dev/null +++ b/android/app/src/main/java/com/retrodev/blastem/BlastEmActivity.java @@ -0,0 +1,158 @@ +package com.retrodev.blastem; +import org.libsdl.app.SDLActivity; +import android.content.ContentResolver; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.ParcelFileDescriptor; +import android.provider.DocumentsContract; +import android.util.Log; +import android.view.View; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; + + +public class BlastEmActivity extends SDLActivity +{ + static final int DOC_TREE_CODE = 4242; + boolean chooseDirInProgress = false; + String chooseDirResult = null; + Map uriMap = new HashMap(); + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //set immersive mode on devices that support it + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + View blah = mSurface; + blah.setSystemUiVisibility( + View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + ); + } + } + + public String getRomPath() { + if (chooseDirInProgress) { + if (chooseDirResult != null) { + chooseDirInProgress = false; + return chooseDirResult; + } + return null; + } + String extStorage = Environment.getExternalStorageDirectory().getAbsolutePath(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + return extStorage; + } + chooseDirInProgress = true; + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + //intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Uri.parse(extStorage)); + /*intent.putExtra("android.content.extra.SHOW_ADVANCED", true); + intent.putExtra("android.content.extra.FANCY", true); + intent.putExtra("android.content.extra.SHOW_FILESIZE", true);*/ + startActivityForResult(intent, DOC_TREE_CODE); + return null; + } + + public String[] readUriDir(String uriString) { + Uri uri = uriMap.get(uriString); + if (uri == null) { + return new String[0]; + } + //adapted from some androidx.documentfile + final ContentResolver resolver = getContentResolver(); + final ArrayList results = new ArrayList<>(); + + Cursor c = null; + try { + Log.i("BlastEm", "getTreeDocumentId: " + DocumentsContract.getTreeDocumentId(uri)); + final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, + DocumentsContract.getTreeDocumentId(uri) + ); + c = resolver.query( + childrenUri, new String[] { + DocumentsContract.Document.COLUMN_DOCUMENT_ID, + DocumentsContract.Document.COLUMN_DISPLAY_NAME, + DocumentsContract.Document.COLUMN_MIME_TYPE + }, null, null, null + ); + while (c.moveToNext()) { + final String documentId = c.getString(0); + String name = c.getString(1); + final String mime = c.getString(2); + final Uri documentUri = DocumentsContract.buildDocumentUriUsingTree(uri, + documentId); + uriMap.put(uriString + "/" + name, documentUri); + if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mime)) { + name += "/"; + } + results.add(name); + } + } catch (Exception e) { + Log.w("BlastEm", "Failed query: " + e); + } finally { + if (c != null) { + c.close(); + } + } + return results.toArray(new String[0]); + } + + public int openUriAsFd(String uriString, String mode) { + Uri uri = uriMap.get(uriString); + if (uri == null) { + Log.w("BlastEm", "Did not find URI in map: " + uriString); + return 0; + } + if (mode.equals("rb")) { + mode = "r"; + } else if (mode.equals("wb")) { + mode = "w"; + } + try { + ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, mode); + if (pfd != null) { + return pfd.detachFd(); + } + Log.w("BlastEm", "openFileDescriptor returned null: " + uriString); + } catch (FileNotFoundException e) { + Log.w("BlastEm", "Failed to open URI: " + e); + } catch (IllegalArgumentException e) { + Log.w("BlastEm", "Failed to open URI: " + e); + } + return 0; + } + + public String[] getAssetsList(String path) { + try { + return getAssets().list(path); + } catch (IOException e) { + Log.w("BlastEm", "Failed to get assets at '" + path + "': " + e); + } + return new String[0]; + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent resultData) { + if (requestCode == DOC_TREE_CODE) { + if (resultCode == RESULT_OK && resultData != null) { + Uri uri = resultData.getData(); + getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + chooseDirResult = uri.toString(); + uriMap.put(chooseDirResult, uri); + Log.i("BlastEm", "ACTION_OPEN_DOCUMENT_TREE got URI " + chooseDirResult); + } else { + Log.i("BlastEm", "ACTION_OPEN_DOCUMENT_TREE failed! "); + chooseDirResult = ""; + } + } else { + super.onActivityResult(requestCode, resultCode, resultData); + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/org b/android/app/src/main/java/org new file mode 120000 index 0000000..ca0e07f --- /dev/null +++ b/android/app/src/main/java/org @@ -0,0 +1 @@ +../../../jni/SDL/android-project/app/src/main/java/org \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..cff44679a38f99e8622931bba13e10475770dc70 GIT binary patch literal 834 zcmV-I1HJr-P)EX>4Tx04R}tkv&MmP!xqvQ?()$hjtKg$WWc^VnrNl6^c+H)C#RSn7s54ni!H4 z7e~Rh;NZ_<)xpJCR|i)?5c~mg7n~Gbq{RD@LW>w5T;9id?;P&C4-lF)rdd5nK+|nA zn@)-O!ipGrMGz6H1o~uV8MBgx##3oXai?JTq$MvU9{yVzJ!CN*A+=sS!^R$5c(Hd?D|# z%6W^kR;#mipZta4qQ1Pub*fRMv4BNL5TT%sDr&G1r(Gw-LYB@`KK>EcFOf?jR~3vL z^Vom}+4Y0}!SC5xrNpF{6ixxXFOKsu284EjR>N_=j~%CV0tBCdE4}02XaX~zq>R z>VLCE)#xzNr92Zh?VSZ*J{#oIU_KnpNfoI5-5x z%apz5^X~rcx&3>mJ-;7qIC7*T;r=lI000JJOGiWi{{a60|De66lK=n!32;bRa{vGY z>;M1+>;Z~y{c->R00(qQO+^Rk0~Zw>E?2$86#xJL8FWQhbVF}#ZDnqB07G(RVRU6= zAa`kWXdp*PO;A^X4i^9b0UJp~K~#9!?b<;Of-n?CQ7~?+YXjEC!f>k5n*Ovv(esAH z6ms)!ekqPArEQRGZV&(j5I_I{1Q0*~2|xe=1Q0+M%e{w(Sl6}5Iw>U$q8P=#YZX1- z95ZXS(oKT^s>^ufp?#57b8yl5vu>@Xn+5^Yxnj{xU~jr<5I~(MR-)1t)Z`^o=K8X) zZW;v80qRV%GD z2%sNOtY$Mg?5vdr0d%2_;#q2F5I_I{1Q0+15I_I{1Q0*~0ranV00hEK#NeyCa{vGU M07*qoM6N<$f_YF~)c^nh literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4261c7601823a6a445be4cb5b4730772e519aa1c GIT binary patch literal 755 zcmVEX>4Tx04R}tkv&MmP!xqvQ?()$hjtKg$WWc^VnrNl6^c+H)C#RSn7s54ni!H4 z7e~Rh;NZ_<)xpJCR|i)?5c~mg7n~Gbq{RD@LW>w5T;9id?;P&C4-lF)rdd5nK+|nA zn@)-O!ipGrMGz6H1o~uV8MBgx##3oXai?JTq$MvU9{yVzJ!CN*A+=sS!^R$5c(Hd?D|# z%6W^kR;#mipZta4qQ1Pub*fRMv4BNL5TT%sDr&G1r(Gw-LYB@`KK>EcFOf?jR~3vL z^Vom}+4Y0}!SC5xrNpF{6ixxXFOKsu284EjR>N_=j~%CV0tBCdE4}02XaX~zq>R z>VLCE)#xzNr92Zh?VSZ*J{#oIU_KnpNfoI5-5x z%apz5^X~rcx&3>mJ-;7qIC7*T;r=lI000JJOGiWi{{a60|De66lK=n!32;bRa{vGY z>;M1+>;Z~y{c->R00(qQO+^Rk0~Zw-D?_{Wo&W#<8FWQhbVF}#ZDnqB07G(RVRU6= zAa`kWXdp*PO;A^X4i^9b0L)25K~!ko?U+Fh!ypUU~JBGb`J1#c2+1T%1_J8No8Qr zm{>c}*5h!1NUMMFRwdE)GJ(Pq0dwCZxyodVb_FP2;aY1oW6u}a{7K+aiiCuE!_avuo3Hg|N$1C5@Ia0bRaLL97R}X=O3;MLOHV7_{RX^%9?Od?kKf&wz6TKC~ zMSEmE-)LaF`L+4~!S^?#mL6F$UBr)ZuYTWWL$;Q(#<0VM`}$>db#a>`CN;lCCU%~83JR4Ey>&6h2cL4F4((#GEjuG zz$3Dlfq~@>2s3u&{Vf6tvX^-Jy0X7y78e!fpW}0)1t=t0;u=xnoS&PUnpeW$T$Gwv zlA5AWo>`Ki;O^-g5Z=fq4vg(6PZ!6Kid%2*dh<0o2)G6s=B&Q%w%JUqEts88tK{N^ zqIWm^kI$|;-^H>#=+wVMse5W!SOgfDI2agN8W@-y7#I~E7@kp|`@Wr49yx1ugmE zI|AMG(}We8fQEYgxKxyD<*=n-`h&;U_8rXung_ID=b5|PugJ}QJ?YOK)3RrO9SmnP z2)ZzE1RT1axALazEZ;3Nd>c5J5H8|3d${u9(eE$RVkVoPeWP1t%BaM_BG6D1Goy4% z!RwFuI(lb>92FWEmP{+?-^3?g;cHhC^l%n~APW=60rR`>XM9~f<<1&rkn<9R_X(Eo z%1sS}Neign2|8oDtsLlFpyxAESJVeuZRSC-zJWo&f#KXU=DF^^UrgU@5oQ1aPgg&e IbxsLQ04PRmZU6uP literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..b625c104f9048e56eb1d3395ac0ceca6d96fc901 GIT binary patch literal 1026 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q4M;wBd$farfw4K$**U<|*;%2WC_gPTCzXLg zV`A+@TaUv5BCY-;LuONT$eoTDbbv%F0X+3Tm)ZM<{TOMG+JNOr=SbgBfk<#R2HmMA~`X9$cHwj^(N7l!{JxM1({$v_d# z0*}aI1_qWlAk5g6_qPZr$X?><>&pI;SzJ_sv44KUW}uK{iEBiObAE1aYF-J0b5Uwy zNotBhd1gt5g1e`0KzJjcI54(vd%8G=RNQ)d=VETNgMe$`hK%fN=B&jAyP4Ln*_d|2 z_4yxlqjOgmi3_b?yY)MhqkcXklam7n2V-M{fB=xCpuo}sWB~;f6L3HaMrp+M`v016cFM{+k@zEm zvwzsm6u!?1G8d>oLE+4gmX&g;kE|ODxSfIOfo2&z4t`c~s?}xrFS$Rvir00%2id_0 zcEO@c{{N-zuLytWIVrnUy#Bm#+1vLm$O@8fNX|Ffx%&B!e+~1)N}#5qc%!Ak!QslU z-u>>jiYcA* zKQc=etg>!|2dm`&hR*o6>sG#Ffd}|vV7MlqO9loUNGH%84xsQ;Q~>J%ha_BgL&YY3 X5of=UzT9)A3_#%N>gTe~DWM4f>R_D_ literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..44a15f0eb02f1ccb1b2746ed640e75519480ff77 GIT binary patch literal 1201 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE4M+yv$zf+;U~JBGb`J1#c2+1T%1_J8No8Qr zm{>c}*5h!1NUMMFRwdE)GJ(Pq0dwCZxyodVb_FP2;aY1oW6u}a{7K+aiiCuE!_avuo3Hg|N$1C5@Ia0bRaLL97R}X=O3;MLOHV7_{RX^%9?Od?kKf&wz6TKC~ zMSEmE-)LaF`L+4~!S^?#mL6F$UBr)ZuYTWWL$;Q(#<0VM`}$>db#a>`CN;lCCU%~83JR4Ey>&6h2cL4F4((#GEjuG zz$3Dlfq~@>2s3u&{Vf6tvX^-Jy0X7y78ez;;J;(%0Thxfag8W(&d<$F%`0JWE=o-- zNlj5G&n(GMaQE~L2yf&QXJBCJ@^o1A&H%K^w1~6`Rp}@CDP_C#Tr$ z&Q$N$cA8UTbV+oR%aNa~`d@Q71sFIOSQwZX7#kQI7!(-rb5|s6Ww~zA_c-*uxzZbS z)d8)ZAM8{8D^~w^xIJl)-c_|@WehP)jmX9}XzB_UMzu8x-&_0RkjcFJlm2Wf_w9Mh zu!DsO#s1(~bwV+JHP5OYtF2PUX3W&tEd8_Y@I|-i{;Zh)a|L@WeLYw vqbG*-+oGDXZAy)@*ojLZ(B#v=SjQl`-*3yr+jo + + #3F51B5 + #303F9F + #FF4081 + diff --git a/android/res/values/strings.xml b/android/app/src/main/res/values/strings.xml similarity index 64% rename from android/res/values/strings.xml rename to android/app/src/main/res/values/strings.xml index 50b31ce..b33e733 100644 --- a/android/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,4 +1,3 @@ - BlastEm diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..7456b1b --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/android/assets/default.cfg b/android/assets/default.cfg deleted file mode 100644 index 3b62540..0000000 --- a/android/assets/default.cfg +++ /dev/null @@ -1,395 +0,0 @@ - -bindings { - keys { - up gamepads.1.up - down gamepads.1.down - left gamepads.1.left - right gamepads.1.right - a gamepads.1.a - s gamepads.1.b - d gamepads.1.c - q gamepads.1.x - w gamepads.1.y - e gamepads.1.z - f gamepads.1.mode - enter gamepads.1.start - - r ui.release_mouse - [ ui.vdp_debug_mode - u ui.enter_debugger - p ui.screenshot - b ui.plane_debug - v ui.vram_debug - c ui.cram_debug - n ui.compositing_debug - esc ui.exit - ` ui.save_state - 0 ui.set_speed.0 - 1 ui.set_speed.1 - 2 ui.set_speed.2 - 3 ui.set_speed.3 - 4 ui.set_speed.4 - 5 ui.set_speed.5 - 6 ui.set_speed.6 - 7 ui.set_speed.7 - = ui.next_speed - - ui.prev_speed - f11 ui.toggle_fullscreen - tab ui.soft_reset - f5 ui.reload - z ui.sms_pause - rctrl ui.toggle_keyboard_captured - - - select gamepads.1.c - play gamepads.1.start - back ui.exit - } - pads { - default { - dpads { - 0 { - up gamepads.n.up - down gamepads.n.down - left gamepads.n.left - right gamepads.n.right - } - } - buttons { - a gamepads.n.a - b gamepads.n.b - rightshoulder gamepads.n.c - x gamepads.n.x - y gamepads.n.y - leftshoulder gamepads.n.z - back gamepads.n.mode - start gamepads.n.start - guide ui.exit - leftstick ui.save_state - } - axes { - lefty.positive gamepads.n.down - lefty.negative gamepads.n.up - leftx.positive gamepads.n.right - leftx.negative gamepads.n.left - lefttrigger ui.prev_speed - righttrigger ui.next_speed - } - } - ps4_6b_right { - axes { - lefttrigger ui.next_speed - leftx.negative gamepads.n.up - leftx.positive gamepads.n.down - lefty.negative gamepads.n.left - lefty.positive gamepads.n.right - righttrigger gamepads.n.c - } - buttons { - a gamepads.n.a - b gamepads.n.b - back ui.sms_pause - guide ui.exit - leftshoulder gamepads.n.mode - leftstick ui.save_state - rightshoulder gamepads.n.z - rightstick ui.prev_speed - start gamepads.n.start - x gamepads.n.x - y gamepads.n.y - } - dpads { - 0 { - down gamepads.n.down - left gamepads.n.left - right gamepads.n.right - up gamepads.n.up - } - } - } - ps3_6b_right { - axes { - lefttrigger ui.next_speed - leftx.negative gamepads.n.up - leftx.positive gamepads.n.down - lefty.negative gamepads.n.left - lefty.positive gamepads.n.right - righttrigger gamepads.n.c - } - buttons { - a gamepads.n.a - b gamepads.n.b - back ui.sms_pause - guide ui.exit - leftshoulder gamepads.n.mode - leftstick ui.save_state - rightshoulder gamepads.n.z - rightstick ui.prev_speed - start gamepads.n.start - x gamepads.n.x - y gamepads.n.y - } - dpads { - 0 { - down gamepads.n.down - left gamepads.n.left - right gamepads.n.right - up gamepads.n.up - } - } - } - xbox_360_6b_right { - axes { - lefttrigger ui.next_speed - leftx.negative gamepads.n.up - leftx.positive gamepads.n.down - lefty.negative gamepads.n.left - lefty.positive gamepads.n.right - righttrigger gamepads.n.c - } - buttons { - a gamepads.n.a - b gamepads.n.b - back ui.sms_pause - guide ui.exit - leftshoulder gamepads.n.mode - leftstick ui.save_state - rightshoulder gamepads.n.z - rightstick ui.prev_speed - start gamepads.n.start - x gamepads.n.x - y gamepads.n.y - } - dpads { - 0 { - down gamepads.n.down - left gamepads.n.left - right gamepads.n.right - up gamepads.n.up - } - } - } - xbone_6b_right { - axes { - lefttrigger ui.next_speed - leftx.negative gamepads.n.up - leftx.positive gamepads.n.down - lefty.negative gamepads.n.left - lefty.positive gamepads.n.right - righttrigger gamepads.n.c - } - buttons { - a gamepads.n.a - b gamepads.n.b - back ui.sms_pause - guide ui.exit - leftshoulder gamepads.n.mode - leftstick ui.save_state - rightshoulder gamepads.n.z - rightstick ui.prev_speed - start gamepads.n.start - x gamepads.n.x - y gamepads.n.y - } - dpads { - 0 { - down gamepads.n.down - left gamepads.n.left - right gamepads.n.right - up gamepads.n.up - } - } - } - genesis_6b_bumpers { - axes { - lefttrigger ui.exit - righttrigger gamepads.n.mode - } - buttons { - a gamepads.n.a - b gamepads.n.b - back ui.sms_pause - guide ui.exit - leftshoulder gamepads.n.z - rightshoulder gamepads.n.c - start gamepads.n.start - x gamepads.n.x - y gamepads.n.y - } - dpads { - 0 { - down gamepads.n.down - left gamepads.n.left - right gamepads.n.right - up gamepads.n.up - } - } - } - saturn_6b_bumpers { - axes { - lefttrigger ui.exit - righttrigger gamepads.n.mode - } - buttons { - a gamepads.n.a - b gamepads.n.b - back ui.sms_pause - guide ui.exit - leftshoulder gamepads.n.z - rightshoulder gamepads.n.c - start gamepads.n.start - x gamepads.n.x - y gamepads.n.y - } - dpads { - 0 { - down gamepads.n.down - left gamepads.n.left - right gamepads.n.right - up gamepads.n.up - } - } - } - } - mice { - 0 { - motion mouse.1.motion - buttons { - 1 mouse.1.left - 2 mouse.1.middle - 3 mouse.1.right - 4 mouse.1.start - } - } - #having the second host mouse also mapped to the first emulated - #mouse is useful for laptop users with an external mouse - 1 { - motion mouse.1.motion - buttons { - 1 mouse.1.left - 2 mouse.1.middle - 3 mouse.1.right - 4 mouse.1.start - } - } - } -} - -io { - devices { - 1 gamepad6.1 - 2 gamepad6.2 - } -} - -video { - #special value "stretch" will cause aspect to match window aspect ratio - aspect 4:3 - width 640 - #height is normally calculated automatically from width using the aspect setting - #if you would like to set it explicitly, uncomment the line below - #height 480 - vertex_shader default.v.glsl - fragment_shader default.f.glsl - scanlines off - vsync off - fullscreen off - #setting gl to off, will force use of the SDL2 fallback renderer - #this is useful for those running on machines with Open GL 2.0 unavailable - #so the warning doesn't display on startup - gl on - #scaling can be linear (for linear interpolation) or nearest (for nearest neighbor) - scaling linear - ntsc { - overscan { - #these values will result in square pixels in H40 mode - top 2 - bottom 1 - #if you want to completely hide the border instead - #comment out those two lines and uncomment these - #top 11 - #bottom 8 - - #these values will completely hide the horizontal border - left 13 - right 14 - } - } - pal { - overscan { - #these values will produce the same size border in V30 mode - #as the default NTSC settings will produce in V24 mode - #this results in a slightly vertically squished picture - #which is probably approximately correct on a properly calibrated TV - top 21 - bottom 17 - #for square pixels and zero border in V30 mode - #coment out those two lines and uncomment these - #top 30 - #bottom 24 - - #these values will completely hide the horizontal border - left 13 - right 14 - } - } -} - -audio { - rate 48000 - buffer 512 - lowpass_cutoff 3390 -} - -clocks { - m68k_divider 7 - max_cycles 3420 - speeds { - 0 100 - 1 150 - 2 200 - 3 300 - 4 400 - 5 25 - 6 50 - 7 75 - } -} - -ui { - #specifies the ROM that implements the Menu UI - rom menu.bin - #starting path for ROM browsing, accepts special variables $HOME, $EXEDIR - #and variables defined in the OS environment - initial_path $HOME - #if this is set to on, then the menu will remember the last path when visited - #if it's set to off, initial_path will always be used on startup - remember_path on - #path for storing internal screenshots, accepts the same variables as initial_path - screenshot_path $HOME - #see strftime for the format specifiers valid in screenshot_template - screenshot_template blastem_%Y%m%d_%H%M%S.png - #path template for saving SRAM, EEPROM and savestates - #accepts special variables $HOME, $EXEDIR, $USERDATA, $ROMNAME - save_path $USERDATA/blastem/$ROMNAME - #space delimited list of file extensions to filter against in menu - extensions bin gen md smd sms gg zip gz - #specifies the preferred save-state format, set to gst for Genecyst compatible states - state_format native -} - -system { - #controls how the emulated system is synced to the host - #video provides the smoothest experience when the host and emulated system have similar refresh rates - #audio provides lower audio latency, especially when there is a refresh rate mismatch - sync_source audio - #set this to random to debug initialization bugs - ram_init zero - default_region U - #controls whether MegaWiFi support is enabled or not - #MegaWiFi allows ROMs to make connections to the internet - #so it should only be enabled for ROMs you trust - megawifi off -} - - diff --git a/android/assets/images b/android/assets/images deleted file mode 120000 index 5fa6987..0000000 --- a/android/assets/images +++ /dev/null @@ -1 +0,0 @@ -../../images \ No newline at end of file diff --git a/android/assets/menu.bin b/android/assets/menu.bin deleted file mode 120000 index eb90371..0000000 --- a/android/assets/menu.bin +++ /dev/null @@ -1 +0,0 @@ -../../menu.bin \ No newline at end of file diff --git a/android/assets/rom.db b/android/assets/rom.db deleted file mode 120000 index 57d3e5a..0000000 --- a/android/assets/rom.db +++ /dev/null @@ -1 +0,0 @@ -../../rom.db \ No newline at end of file diff --git a/android/assets/shaders b/android/assets/shaders deleted file mode 120000 index 704ee39..0000000 --- a/android/assets/shaders +++ /dev/null @@ -1 +0,0 @@ -../../shaders \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..2c911c6 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,25 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + mavenCentral() + google() + } + dependencies { + classpath 'com.android.tools.build:gradle:8.1.1' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + mavenCentral() + google() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android/build.properties b/android/build.properties deleted file mode 100644 index edc7f23..0000000 --- a/android/build.properties +++ /dev/null @@ -1,17 +0,0 @@ -# This file is used to override default values used by the Ant build system. -# -# This file must be checked in Version Control Systems, as it is -# integral to the build system of your project. - -# This file is only used by the Ant script. - -# You can use this to override default values such as -# 'source.dir' for the location of your java source folder and -# 'out.dir' for the location of your output folder. - -# You can also use it define how the release builds are signed by declaring -# the following properties: -# 'key.store' for the location of your keystore and -# 'key.alias' for the name of the key to use. -# The password will be asked during the build when you use the 'release' target. - diff --git a/android/build.xml b/android/build.xml deleted file mode 100644 index 8cc29de..0000000 --- a/android/build.xml +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/default.properties b/android/default.properties deleted file mode 100644 index 0cdab95..0000000 --- a/android/default.properties +++ /dev/null @@ -1,11 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system use, -# "build.properties", and override values to adapt the script to your -# project structure. - -# Project target. -target=android-12 diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..aac7c9b --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,17 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..2b338a935b508eb6286ed4b84a91176ee5fb93c5 GIT binary patch literal 54213 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 W0pC&I?SueJAtDe^1>pYz0sTMJrJ5N4 literal 0 HcmV?d00001 diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..5b9d759 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Nov 11 18:20:34 PST 2021 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/android/gradlew b/android/gradlew new file mode 100755 index 0000000..3427607 --- /dev/null +++ b/android/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|grep -E -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|grep -E -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/jni/Application.mk b/android/jni/Application.mk deleted file mode 100644 index 053c0f1..0000000 --- a/android/jni/Application.mk +++ /dev/null @@ -1,8 +0,0 @@ - -# Uncomment this if you're using STL in your project -# See CPLUSPLUS-SUPPORT.html in the NDK documentation for more information -# APP_STL := stlport_static - -APP_ABI := x86 -APP_PLATFORM := android-16 -APP_OPTIM := release diff --git a/android/jni/src b/android/jni/src deleted file mode 120000 index c25bddb..0000000 --- a/android/jni/src +++ /dev/null @@ -1 +0,0 @@ -../.. \ No newline at end of file diff --git a/android/proguard-project.txt b/android/proguard-project.txt deleted file mode 100644 index f2fe155..0000000 --- a/android/proguard-project.txt +++ /dev/null @@ -1,20 +0,0 @@ -# To enable ProGuard in your project, edit project.properties -# to define the proguard.config property as described in that file. -# -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in ${sdk.dir}/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the ProGuard -# include property in project.properties. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/android/project.properties b/android/project.properties deleted file mode 100644 index 916037e..0000000 --- a/android/project.properties +++ /dev/null @@ -1,14 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-23 diff --git a/android/res/drawable-hdpi/ic_launcher.png b/android/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index 83ce8f1dad2e791633fb89dd41407b1510e541d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 306 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!oCO|{#S9E8FF}|wK;`)lpx_fv z7srr_TW@b?^BqzUaD8}f$NNiDAM=G68H>Fs|ECq0_iSE6e=aiv!w&V|EbkYd-*dcO zQgA}Uzq5{egFLD~&GVhmz_Mje^RYRXo7P_|nw!!T;^T1Ia>a7JeK$PSJdU!M%x$i^ zIeCBKxsMM&8eazrIWTFJ2fW*<`dg-$%VB`dGa9ayU6?TWk6YkTmLrb# v$LdXi*6+CFc=ML#-Nl?j6B>X%3^>4Dbw@Rkk4ZcU$Ybzy^>bP0l+XkKJBNRj diff --git a/android/res/drawable-mdpi/ic_launcher.png b/android/res/drawable-mdpi/ic_launcher.png deleted file mode 100644 index 09b9aeeb7902c6e7baa361a651b4077d9ede2aae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?3@*&xj5S=u}WD7eVe z#W5t~-rE_0LI(tRSYLj*XHi+zxJ^Ye^v&;cGd`GcIyrDGoY*z@+SK)*tO^zseR|}t zruUYCBXr}*uBk<)D>Sf*Ao@#Qz>#_fkC>^eUw&R` zxNggNP64U4m+uDZbYui*cUvD|yTMS*(6XbqNcySg$;m|x%hifp^}&<4E;KnXFh-cR+-5T8j|j}Ww`K{`76kzZhIwL#RG+j| zUU*gfe|4JZ0#-qwz5|DX17?;Lygl-H_s?C%c}78uQ38w#5AG_;eE#atFYs1&+txX` z?=@F4=yI?KG}!OEyxvdQxx{71d@ToVCPtP91}30g3JnZ*j{j|ty2B5)`i0D)g)?t| zeQ-v#k&Z67FAfV7PU+&Dt w*^f@{j1u5bV2ExlNO|UWS0Cz31~54AFDB`-YWp9pH5);EPgg&ebxsLQ0H<<@#sB~S diff --git a/android/res/drawable-xxhdpi/ic_launcher.png b/android/res/drawable-xxhdpi/ic_launcher.png deleted file mode 100644 index bbd6fc55f146c9c09f470e7b93df30c44c332f44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 484 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q4M;wBd$a>caTa()7Bet-UIJmp6|P-B7#J9* zdAc};RNQ)dH+$V71%ZY@gYx^b;pbwRI#XH>7T4XrHcfwmfc#oUpms3W{zA@C`q+nJ z{l5wdEG-QJ0vsHSfk*Y?R1gqgTGS)`t+D8PWzy%nuD=-c z<{si{QRM+jv9K^XIZO#ml+Tc<{dnU~iBfIDZb=Sr37{z0)E$S+zcsquoVBW7m>7YUw=_7wUHC|2pZK;@-wz!SY6JQOET8qk`l9j1iaqx` z+2E=>jDOt!akcySv*p4sncvhu-q-^51V@MU7w&h7OXOr`zt_9OUhq;RO+hCS;s1Zk z-z~Pu)*NG4+P6^>6eU|p{ENgaBwsPWz>i(^E-9)Prz-Y=VvoVo)z4*} HQ$iB}@A9kM diff --git a/android/res/layout/main.xml b/android/res/layout/main.xml deleted file mode 100644 index 123c4b6..0000000 --- a/android/res/layout/main.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/android/src/com/retrodev/blastem/BlastEmActivity.java b/android/src/com/retrodev/blastem/BlastEmActivity.java deleted file mode 100644 index 441e94a..0000000 --- a/android/src/com/retrodev/blastem/BlastEmActivity.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.retrodev.blastem; -import org.libsdl.app.SDLActivity; -import android.os.Build; -import android.os.Bundle; -import android.view.View; - - -public class BlastEmActivity extends SDLActivity -{ - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - //set immersive mode on devices that support it - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - View blah = mSurface; - blah.setSystemUiVisibility( - View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - ); - } - } -} \ No newline at end of file diff --git a/android/src/org/libsdl/app/SDL.java b/android/src/org/libsdl/app/SDL.java deleted file mode 100644 index cfe4830..0000000 --- a/android/src/org/libsdl/app/SDL.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.libsdl.app; - -import android.content.Context; - -/** - SDL library initialization -*/ -public class SDL { - - // This function should be called first and sets up the native code - // so it can call into the Java classes - public static void setupJNI() { - SDLActivity.nativeSetupJNI(); - SDLAudioManager.nativeSetupJNI(); - SDLControllerManager.nativeSetupJNI(); - } - - // This function should be called each time the activity is started - public static void initialize() { - setContext(null); - - SDLActivity.initialize(); - SDLAudioManager.initialize(); - SDLControllerManager.initialize(); - } - - // This function stores the current activity (SDL or not) - public static void setContext(Context context) { - mContext = context; - } - - public static Context getContext() { - return mContext; - } - - protected static Context mContext; -} diff --git a/android/src/org/libsdl/app/SDLActivity.java b/android/src/org/libsdl/app/SDLActivity.java deleted file mode 100644 index 9119473..0000000 --- a/android/src/org/libsdl/app/SDLActivity.java +++ /dev/null @@ -1,1595 +0,0 @@ -package org.libsdl.app; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.lang.reflect.Method; -import java.util.Objects; - -import android.app.*; -import android.content.*; -import android.text.InputType; -import android.view.*; -import android.view.inputmethod.BaseInputConnection; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputMethodManager; -import android.widget.RelativeLayout; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.os.*; -import android.util.Log; -import android.util.SparseArray; -import android.graphics.*; -import android.graphics.drawable.Drawable; -import android.hardware.*; -import android.content.pm.ActivityInfo; - -/** - SDL Activity -*/ -public class SDLActivity extends Activity { - private static final String TAG = "SDL"; - - public static boolean mIsResumedCalled, mIsSurfaceReady, mHasFocus; - - // Handle the state of the native layer - public enum NativeState { - INIT, RESUMED, PAUSED - } - - public static NativeState mNextNativeState; - public static NativeState mCurrentNativeState; - - public static boolean mExitCalledFromJava; - - /** If shared libraries (e.g. SDL or the native application) could not be loaded. */ - public static boolean mBrokenLibraries; - - // If we want to separate mouse and touch events. - // This is only toggled in native code when a hint is set! - public static boolean mSeparateMouseAndTouch; - - // Main components - protected static SDLActivity mSingleton; - protected static SDLSurface mSurface; - protected static View mTextEdit; - protected static boolean mScreenKeyboardShown; - protected static ViewGroup mLayout; - protected static SDLClipboardHandler mClipboardHandler; - - - // This is what SDL runs in. It invokes SDL_main(), eventually - protected static Thread mSDLThread; - - /** - * This method returns the name of the shared object with the application entry point - * It can be overridden by derived classes. - */ - protected String getMainSharedObject() { - String library; - String[] libraries = SDLActivity.mSingleton.getLibraries(); - if (libraries.length > 0) { - library = "lib" + libraries[libraries.length - 1] + ".so"; - } else { - library = "libmain.so"; - } - return library; - } - - /** - * This method returns the name of the application entry point - * It can be overridden by derived classes. - */ - protected String getMainFunction() { - return "SDL_main"; - } - - /** - * This method is called by SDL before loading the native shared libraries. - * It can be overridden to provide names of shared libraries to be loaded. - * The default implementation returns the defaults. It never returns null. - * An array returned by a new implementation must at least contain "SDL2". - * Also keep in mind that the order the libraries are loaded may matter. - * @return names of shared libraries to be loaded (e.g. "SDL2", "main"). - */ - protected String[] getLibraries() { - return new String[] { - "SDL2", - // "SDL2_image", - // "SDL2_mixer", - // "SDL2_net", - // "SDL2_ttf", - "main" - }; - } - - // Load the .so - public void loadLibraries() { - for (String lib : getLibraries()) { - System.loadLibrary(lib); - } - } - - /** - * This method is called by SDL before starting the native application thread. - * It can be overridden to provide the arguments after the application name. - * The default implementation returns an empty array. It never returns null. - * @return arguments for the native application. - */ - protected String[] getArguments() { - return new String[0]; - } - - public static void initialize() { - // The static nature of the singleton and Android quirkyness force us to initialize everything here - // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values - mSingleton = null; - mSurface = null; - mTextEdit = null; - mLayout = null; - mClipboardHandler = null; - mSDLThread = null; - mExitCalledFromJava = false; - mBrokenLibraries = false; - mIsResumedCalled = false; - mIsSurfaceReady = false; - mHasFocus = true; - mNextNativeState = NativeState.INIT; - mCurrentNativeState = NativeState.INIT; - } - - // Setup - @Override - protected void onCreate(Bundle savedInstanceState) { - Log.v(TAG, "Device: " + android.os.Build.DEVICE); - Log.v(TAG, "Model: " + android.os.Build.MODEL); - Log.v(TAG, "onCreate()"); - super.onCreate(savedInstanceState); - - // Load shared libraries - String errorMsgBrokenLib = ""; - try { - loadLibraries(); - } catch(UnsatisfiedLinkError e) { - System.err.println(e.getMessage()); - mBrokenLibraries = true; - errorMsgBrokenLib = e.getMessage(); - } catch(Exception e) { - System.err.println(e.getMessage()); - mBrokenLibraries = true; - errorMsgBrokenLib = e.getMessage(); - } - - if (mBrokenLibraries) - { - AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this); - dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall." - + System.getProperty("line.separator") - + System.getProperty("line.separator") - + "Error: " + errorMsgBrokenLib); - dlgAlert.setTitle("SDL Error"); - dlgAlert.setPositiveButton("Exit", - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog,int id) { - // if this button is clicked, close current activity - SDLActivity.mSingleton.finish(); - } - }); - dlgAlert.setCancelable(false); - dlgAlert.create().show(); - - return; - } - - // Set up JNI - SDL.setupJNI(); - - // Initialize state - SDL.initialize(); - - // So we can call stuff from static callbacks - mSingleton = this; - SDL.setContext(this); - - if (Build.VERSION.SDK_INT >= 11) { - mClipboardHandler = new SDLClipboardHandler_API11(); - } else { - /* Before API 11, no clipboard notification (eg no SDL_CLIPBOARDUPDATE) */ - mClipboardHandler = new SDLClipboardHandler_Old(); - } - - // Set up the surface - mSurface = new SDLSurface(getApplication()); - - mLayout = new RelativeLayout(this); - mLayout.addView(mSurface); - - setContentView(mLayout); - - // Get filename from "Open with" of another application - Intent intent = getIntent(); - if (intent != null && intent.getData() != null) { - String filename = intent.getData().getPath(); - if (filename != null) { - Log.v(TAG, "Got filename: " + filename); - SDLActivity.onNativeDropFile(filename); - } - } - } - - // Events - @Override - protected void onPause() { - Log.v(TAG, "onPause()"); - super.onPause(); - mNextNativeState = NativeState.PAUSED; - mIsResumedCalled = false; - - if (SDLActivity.mBrokenLibraries) { - return; - } - - SDLActivity.handleNativeState(); - } - - @Override - protected void onResume() { - Log.v(TAG, "onResume()"); - super.onResume(); - mNextNativeState = NativeState.RESUMED; - mIsResumedCalled = true; - - if (SDLActivity.mBrokenLibraries) { - return; - } - - SDLActivity.handleNativeState(); - } - - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - Log.v(TAG, "onWindowFocusChanged(): " + hasFocus); - - if (SDLActivity.mBrokenLibraries) { - return; - } - - SDLActivity.mHasFocus = hasFocus; - if (hasFocus) { - mNextNativeState = NativeState.RESUMED; - } else { - mNextNativeState = NativeState.PAUSED; - } - - SDLActivity.handleNativeState(); - } - - @Override - public void onLowMemory() { - Log.v(TAG, "onLowMemory()"); - super.onLowMemory(); - - if (SDLActivity.mBrokenLibraries) { - return; - } - - SDLActivity.nativeLowMemory(); - } - - @Override - protected void onDestroy() { - Log.v(TAG, "onDestroy()"); - - if (SDLActivity.mBrokenLibraries) { - super.onDestroy(); - // Reset everything in case the user re opens the app - SDLActivity.initialize(); - return; - } - - mNextNativeState = NativeState.PAUSED; - SDLActivity.handleNativeState(); - - // Send a quit message to the application - SDLActivity.mExitCalledFromJava = true; - SDLActivity.nativeQuit(); - - // Now wait for the SDL thread to quit - if (SDLActivity.mSDLThread != null) { - try { - SDLActivity.mSDLThread.join(); - } catch(Exception e) { - Log.v(TAG, "Problem stopping thread: " + e); - } - SDLActivity.mSDLThread = null; - - //Log.v(TAG, "Finished waiting for SDL thread"); - } - - super.onDestroy(); - - // Reset everything in case the user re opens the app - SDLActivity.initialize(); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - - if (SDLActivity.mBrokenLibraries) { - return false; - } - - int keyCode = event.getKeyCode(); - // Ignore certain special keys so they're handled by Android - if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || - keyCode == KeyEvent.KEYCODE_VOLUME_UP || - keyCode == KeyEvent.KEYCODE_CAMERA || - keyCode == KeyEvent.KEYCODE_ZOOM_IN || /* API 11 */ - keyCode == KeyEvent.KEYCODE_ZOOM_OUT /* API 11 */ - ) { - return false; - } - return super.dispatchKeyEvent(event); - } - - /* Transition to next state */ - public static void handleNativeState() { - - if (mNextNativeState == mCurrentNativeState) { - // Already in same state, discard. - return; - } - - // Try a transition to init state - if (mNextNativeState == NativeState.INIT) { - - mCurrentNativeState = mNextNativeState; - return; - } - - // Try a transition to paused state - if (mNextNativeState == NativeState.PAUSED) { - nativePause(); - mSurface.handlePause(); - mCurrentNativeState = mNextNativeState; - return; - } - - // Try a transition to resumed state - if (mNextNativeState == NativeState.RESUMED) { - if (mIsSurfaceReady && mHasFocus && mIsResumedCalled) { - if (mSDLThread == null) { - // This is the entry point to the C app. - // Start up the C app thread and enable sensor input for the first time - // FIXME: Why aren't we enabling sensor input at start? - - final Thread sdlThread = new Thread(new SDLMain(), "SDLThread"); - mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true); - sdlThread.start(); - - // Set up a listener thread to catch when the native thread ends - mSDLThread = new Thread(new Runnable() { - @Override - public void run() { - try { - sdlThread.join(); - } catch (Exception e) { - // Ignore any exception - } finally { - // Native thread has finished - if (!mExitCalledFromJava) { - handleNativeExit(); - } - } - } - }, "SDLThreadListener"); - - mSDLThread.start(); - } - - nativeResume(); - mSurface.handleResume(); - mCurrentNativeState = mNextNativeState; - } - } - } - - /* The native thread has finished */ - public static void handleNativeExit() { - SDLActivity.mSDLThread = null; - mSingleton.finish(); - } - - - // Messages from the SDLMain thread - static final int COMMAND_CHANGE_TITLE = 1; - static final int COMMAND_UNUSED = 2; - static final int COMMAND_TEXTEDIT_HIDE = 3; - static final int COMMAND_SET_KEEP_SCREEN_ON = 5; - - protected static final int COMMAND_USER = 0x8000; - - /** - * This method is called by SDL if SDL did not handle a message itself. - * This happens if a received message contains an unsupported command. - * Method can be overwritten to handle Messages in a different class. - * @param command the command of the message. - * @param param the parameter of the message. May be null. - * @return if the message was handled in overridden method. - */ - protected boolean onUnhandledMessage(int command, Object param) { - return false; - } - - /** - * A Handler class for Messages from native SDL applications. - * It uses current Activities as target (e.g. for the title). - * static to prevent implicit references to enclosing object. - */ - protected static class SDLCommandHandler extends Handler { - @Override - public void handleMessage(Message msg) { - Context context = SDL.getContext(); - if (context == null) { - Log.e(TAG, "error handling message, getContext() returned null"); - return; - } - switch (msg.arg1) { - case COMMAND_CHANGE_TITLE: - if (context instanceof Activity) { - ((Activity) context).setTitle((String)msg.obj); - } else { - Log.e(TAG, "error handling message, getContext() returned no Activity"); - } - break; - case COMMAND_TEXTEDIT_HIDE: - if (mTextEdit != null) { - // Note: On some devices setting view to GONE creates a flicker in landscape. - // Setting the View's sizes to 0 is similar to GONE but without the flicker. - // The sizes will be set to useful values when the keyboard is shown again. - mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0)); - - InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0); - - mScreenKeyboardShown = false; - } - break; - case COMMAND_SET_KEEP_SCREEN_ON: - { - if (context instanceof Activity) { - Window window = ((Activity) context).getWindow(); - if (window != null) { - if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) { - window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } else { - window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } - } - } - break; - } - default: - if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) { - Log.e(TAG, "error handling message, command is " + msg.arg1); - } - } - } - } - - // Handler for the messages - Handler commandHandler = new SDLCommandHandler(); - - // Send a message from the SDLMain thread - boolean sendCommand(int command, Object data) { - Message msg = commandHandler.obtainMessage(); - msg.arg1 = command; - msg.obj = data; - return commandHandler.sendMessage(msg); - } - - // C functions we call - public static native int nativeSetupJNI(); - public static native int nativeRunMain(String library, String function, Object arguments); - public static native void nativeLowMemory(); - public static native void nativeQuit(); - public static native void nativePause(); - public static native void nativeResume(); - public static native void onNativeDropFile(String filename); - public static native void onNativeResize(int x, int y, int format, float rate); - public static native void onNativeKeyDown(int keycode); - public static native void onNativeKeyUp(int keycode); - public static native void onNativeKeyboardFocusLost(); - public static native void onNativeMouse(int button, int action, float x, float y); - public static native void onNativeTouch(int touchDevId, int pointerFingerId, - int action, float x, - float y, float p); - public static native void onNativeAccel(float x, float y, float z); - public static native void onNativeClipboardChanged(); - public static native void onNativeSurfaceChanged(); - public static native void onNativeSurfaceDestroyed(); - public static native String nativeGetHint(String name); - - /** - * This method is called by SDL using JNI. - */ - public static boolean setActivityTitle(String title) { - // Called from SDLMain() thread and can't directly affect the view - return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title); - } - - /** - * This method is called by SDL using JNI. - * This is a static method for JNI convenience, it calls a non-static method - * so that is can be overridden - */ - public static void setOrientation(int w, int h, boolean resizable, String hint) - { - if (mSingleton != null) { - mSingleton.setOrientationBis(w, h, resizable, hint); - } - } - - /** - * This can be overridden - */ - public void setOrientationBis(int w, int h, boolean resizable, String hint) - { - int orientation = -1; - - if (!Objects.equals(hint, "")) { - if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) { - orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; - } else if (hint.contains("LandscapeRight")) { - orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - } else if (hint.contains("LandscapeLeft")) { - orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; - } else if (hint.contains("Portrait") && hint.contains("PortraitUpsideDown")) { - orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; - } else if (hint.contains("Portrait")) { - orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; - } else if (hint.contains("PortraitUpsideDown")) { - orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; - } - } - - /* no valid hint */ - if (orientation == -1) { - if (resizable) { - /* no fixed orientation */ - } else { - if (w > h) { - orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; - } else { - orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; - } - } - } - - Log.v("SDL", "setOrientation() orientation=" + orientation + " width=" + w +" height="+ h +" resizable=" + resizable + " hint=" + hint); - if (orientation != -1) { - mSingleton.setRequestedOrientation(orientation); - } - } - - - /** - * This method is called by SDL using JNI. - */ - public static boolean isScreenKeyboardShown() - { - if (mTextEdit == null) { - return false; - } - - if (!mScreenKeyboardShown) { - return false; - } - - InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - return imm.isAcceptingText(); - - } - - /** - * This method is called by SDL using JNI. - */ - public static boolean sendMessage(int command, int param) { - if (mSingleton == null) { - return false; - } - return mSingleton.sendCommand(command, Integer.valueOf(param)); - } - - /** - * This method is called by SDL using JNI. - */ - public static Context getContext() { - return SDL.getContext(); - } - - static class ShowTextInputTask implements Runnable { - /* - * This is used to regulate the pan&scan method to have some offset from - * the bottom edge of the input region and the top edge of an input - * method (soft keyboard) - */ - static final int HEIGHT_PADDING = 15; - - public int x, y, w, h; - - public ShowTextInputTask(int x, int y, int w, int h) { - this.x = x; - this.y = y; - this.w = w; - this.h = h; - } - - @Override - public void run() { - RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(w, h + HEIGHT_PADDING); - params.leftMargin = x; - params.topMargin = y; - - if (mTextEdit == null) { - mTextEdit = new DummyEdit(SDL.getContext()); - - mLayout.addView(mTextEdit, params); - } else { - mTextEdit.setLayoutParams(params); - } - - mTextEdit.setVisibility(View.VISIBLE); - mTextEdit.requestFocus(); - - InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(mTextEdit, 0); - - mScreenKeyboardShown = true; - } - } - - /** - * This method is called by SDL using JNI. - */ - public static boolean showTextInput(int x, int y, int w, int h) { - // Transfer the task to the main thread as a Runnable - return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h)); - } - - public static boolean isTextInputEvent(KeyEvent event) { - - // Key pressed with Ctrl should be sent as SDL_KEYDOWN/SDL_KEYUP and not SDL_TEXTINPUT - if (android.os.Build.VERSION.SDK_INT >= 11) { - if (event.isCtrlPressed()) { - return false; - } - } - - return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE; - } - - /** - * This method is called by SDL using JNI. - */ - public static Surface getNativeSurface() { - if (SDLActivity.mSurface == null) { - return null; - } - return SDLActivity.mSurface.getNativeSurface(); - } - - // Input - - /** - * This method is called by SDL using JNI. - * @return an array which may be empty but is never null. - */ - public static int[] inputGetInputDeviceIds(int sources) { - int[] ids = InputDevice.getDeviceIds(); - int[] filtered = new int[ids.length]; - int used = 0; - for (int i = 0; i < ids.length; ++i) { - InputDevice device = InputDevice.getDevice(ids[i]); - if ((device != null) && ((device.getSources() & sources) != 0)) { - filtered[used++] = device.getId(); - } - } - return Arrays.copyOf(filtered, used); - } - - // APK expansion files support - - /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */ - private static Object expansionFile; - - /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */ - private static Method expansionFileMethod; - - /** - * This method is called by SDL using JNI. - * @return an InputStream on success or null if no expansion file was used. - * @throws IOException on errors. Message is set for the SDL error message. - */ - public static InputStream openAPKExpansionInputStream(String fileName) throws IOException { - // Get a ZipResourceFile representing a merger of both the main and patch files - if (expansionFile == null) { - String mainHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION"); - if (mainHint == null) { - return null; // no expansion use if no main version was set - } - String patchHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION"); - if (patchHint == null) { - return null; // no expansion use if no patch version was set - } - - Integer mainVersion; - Integer patchVersion; - try { - mainVersion = Integer.valueOf(mainHint); - patchVersion = Integer.valueOf(patchHint); - } catch (NumberFormatException ex) { - ex.printStackTrace(); - throw new IOException("No valid file versions set for APK expansion files", ex); - } - - try { - // To avoid direct dependency on Google APK expansion library that is - // not a part of Android SDK we access it using reflection - expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport") - .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class) - .invoke(null, SDL.getContext(), mainVersion, patchVersion); - - expansionFileMethod = expansionFile.getClass() - .getMethod("getInputStream", String.class); - } catch (Exception ex) { - ex.printStackTrace(); - expansionFile = null; - expansionFileMethod = null; - throw new IOException("Could not access APK expansion support library", ex); - } - } - - // Get an input stream for a known file inside the expansion file ZIPs - InputStream fileStream; - try { - fileStream = (InputStream)expansionFileMethod.invoke(expansionFile, fileName); - } catch (Exception ex) { - // calling "getInputStream" failed - ex.printStackTrace(); - throw new IOException("Could not open stream from APK expansion file", ex); - } - - if (fileStream == null) { - // calling "getInputStream" was successful but null was returned - throw new IOException("Could not find path in APK expansion file"); - } - - return fileStream; - } - - // Messagebox - - /** Result of current messagebox. Also used for blocking the calling thread. */ - protected final int[] messageboxSelection = new int[1]; - - /** Id of current dialog. */ - protected int dialogs = 0; - - /** - * This method is called by SDL using JNI. - * Shows the messagebox from UI thread and block calling thread. - * buttonFlags, buttonIds and buttonTexts must have same length. - * @param buttonFlags array containing flags for every button. - * @param buttonIds array containing id for every button. - * @param buttonTexts array containing text for every button. - * @param colors null for default or array of length 5 containing colors. - * @return button id or -1. - */ - public int messageboxShowMessageBox( - final int flags, - final String title, - final String message, - final int[] buttonFlags, - final int[] buttonIds, - final String[] buttonTexts, - final int[] colors) { - - messageboxSelection[0] = -1; - - // sanity checks - - if ((buttonFlags.length != buttonIds.length) && (buttonIds.length != buttonTexts.length)) { - return -1; // implementation broken - } - - // collect arguments for Dialog - - final Bundle args = new Bundle(); - args.putInt("flags", flags); - args.putString("title", title); - args.putString("message", message); - args.putIntArray("buttonFlags", buttonFlags); - args.putIntArray("buttonIds", buttonIds); - args.putStringArray("buttonTexts", buttonTexts); - args.putIntArray("colors", colors); - - // trigger Dialog creation on UI thread - - runOnUiThread(new Runnable() { - @Override - public void run() { - showDialog(dialogs++, args); - } - }); - - // block the calling thread - - synchronized (messageboxSelection) { - try { - messageboxSelection.wait(); - } catch (InterruptedException ex) { - ex.printStackTrace(); - return -1; - } - } - - // return selected value - - return messageboxSelection[0]; - } - - @Override - protected Dialog onCreateDialog(int ignore, Bundle args) { - - // TODO set values from "flags" to messagebox dialog - - // get colors - - int[] colors = args.getIntArray("colors"); - int backgroundColor; - int textColor; - int buttonBorderColor; - int buttonBackgroundColor; - int buttonSelectedColor; - if (colors != null) { - int i = -1; - backgroundColor = colors[++i]; - textColor = colors[++i]; - buttonBorderColor = colors[++i]; - buttonBackgroundColor = colors[++i]; - buttonSelectedColor = colors[++i]; - } else { - backgroundColor = Color.TRANSPARENT; - textColor = Color.TRANSPARENT; - buttonBorderColor = Color.TRANSPARENT; - buttonBackgroundColor = Color.TRANSPARENT; - buttonSelectedColor = Color.TRANSPARENT; - } - - // create dialog with title and a listener to wake up calling thread - - final Dialog dialog = new Dialog(this); - dialog.setTitle(args.getString("title")); - dialog.setCancelable(false); - dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(DialogInterface unused) { - synchronized (messageboxSelection) { - messageboxSelection.notify(); - } - } - }); - - // create text - - TextView message = new TextView(this); - message.setGravity(Gravity.CENTER); - message.setText(args.getString("message")); - if (textColor != Color.TRANSPARENT) { - message.setTextColor(textColor); - } - - // create buttons - - int[] buttonFlags = args.getIntArray("buttonFlags"); - int[] buttonIds = args.getIntArray("buttonIds"); - String[] buttonTexts = args.getStringArray("buttonTexts"); - - final SparseArray